Friday, June 26, 2009

Ico to PNG conversion

http://www.converticon.com for converting icon files to png.

Originally had http://www.icopng.com/index.php, but that seems to be offline, now.

Image editor to extend MS Paint (transparency & animation)

Generally, for my purposes the image editor in MS Paint or Visual Studio is adequate, but both these fail to provide transparency for GIFs or PNG files. http://www.online-image-editor.com/index.cfm is a great way to add this functionality. Looks like it'll also do GIF animations.

TabControl and the Appearance property

How insane. I had set the Appearance property of the Tabcontrol to "Buttons" and thought, "That looks cool!" I also set the Alignment to "Bottom". Something different and pretty slick-looking.

But then I spent the next few hours trying to figure out why my custom control that I dropped on one of these tab pages wouldn't render. I kept thinking there was something wrong with the custom control. Finally, after a Google, http://msdn.microsoft.com/en-us/library/system.windows.forms.tabcontrol.appearance(VS.71).aspx explained it all.

Selecting "Buttons" or "FlatButtons" for the TabControl's Appearance property requires that the Alignment be "Top". Of course, the notation just says that the alignment must be "Top" or the "Buttons" won't display correctly. It doesn't say anything about any other controls, which is what I experienced.

Alignment to "Top" if altering the Appearance on the TabControl to Buttons or FlatButtons.

Wednesday, June 24, 2009

Finding a control within a control

I have a case where I need to get a particular control out of a container of controls. I don't know the name of the control, although I am prefixing it with a specific pattern, because I'm adding the control dynamically, and there could be any number of these controls.

I found Control.ControlCollection.Find, but the Help for it (http://msdn.microsoft.com/en-us/library/system.windows.forms.control.controlcollection.find.aspx) did not give any examples using a wildcard matching. Yes, I could iterate through all controls, but that seems inefficient. Yes, I could use LINQ, but this project has the limitation of being required to be .NET 2.0, not .NET 3.5.

So, time for the sandbox. Simple test: I create a form, with a groupbox (groupBox1) and put three controls in it. One Button control named "button1" and two CheckBox controls named "checkBox1" and "checkBox2". Then ran this code:

Control

[] ctl = groupBox1.Controls.Find("checkBox*", true

);

Control[] ctl1 = groupBox1.Controls.Find("checkBox1", true

);

Control[] ctl2 = groupBox1.Controls.Find("checkBox2", true);

ctl had no elements, while ctl1 and ctl2 both had one element (due to the exact match). I also tried changing "checkBox*" to just "checkBox" with the same result.

Well, this was rather annoying. After all, what's the point of returning an array, if the best you're going to do is return one item?

Then, on an whim I tried the following code:

this

.checkBox1.Name = "test"

;

this.checkBox2.Name = "test"

;

Control[] ctl = groupBox1.Controls.Find("test", false);

And good news! I got two items back!

So the trick in my case was when I go to generate my dynamic control, I always set the "Name" property the same. Then Controls.Find will find all matches, since you cannot wildcard it.


ClickOnce and Team Systems Summary

Had a new, simple ClickOnce App to add to Team Systems, so I'd thought it a good time to summarize what I've learned from previous posts.

1. Log into the build machine as the tfsbuild user.
2. Open the solution from source control in Visual Studio and publish. This sets up the ClickOnce publish key. This step can be circumvented with the use of MAGE or MAGEUI, but who wants to go through all that when this works.
3. Copy a Team Build project from a previous ClickOnce build project (or use some kind of template, or something).
- Set up your landing page
- If the ClickOnce project references a separate project within the solution, be sure to define both the SolutionToBuild and SolutionToPublish tags. (Otherwise, just the SolutionToPublish is needed).
- Define the Application Title, Support URL, Company, ClickOnceURL, and any other application and deployment-specific properties.
- Modify App.config settings in the AfterGet target override. Also, obtain Version definition in the AfterGet.
- In the AfterDropBuild target override, copy all files from the drop folder to the deployment folder(s).

This pretty much summarizes what works for me.

I promised to go through each tag to explain everything. I did stumple onto http://msdn.microsoft.com/en-us/magazine/dd458792.aspx today, which appears to be a terrific tutorial. I wish I had found it a few weeks (or even months) ago. Because of how good this link is, I'm going to supply only the Cliff Notes version.

In detail, the meaning of each tag in a Team Systems Build project:

<Project
Defines the project.
Attributes:
DefaultTargets - Ignored by Team Systems 2008. This is only used by MSBuild if run directly. Team Systems Build always uses only EndToEndIteration. This attribute defines which target MSBuild should use as its main entry point.

<Import
Specifies include projects files, which are used to define extension tasks.
Attributes:
Project - Defines the project file to import.

<RunCodeAnalysis
Defines whether or not the build will run Code Analysis.

<RunTest
<MetaDataFile
<TestList
Enables/disables the running of tests. Personally, I think this is a great feature to have, but unfortunately, it's often quite difficult to put together adequate tests in a system that integrates with user interaction and separate systems, such as the OpenVMS systems my shop uses.

<Message
Adds a message to the build log
Attributes:
Text - the message.

<ItemGroup

<PropertyGroup

<SolutionToBuild
Defines the solution file that should be compiled.

<SolutionToPublish
Defines the solution file that is to be published.

<ConfigurationToBuild
Defines whether the soution should be built for Debug or Release and for what kind of CPU.

<Target
Overridable point that MSBuild runs.
Attributes:
Name - the name of the target.

<UsingTask
Defines a custom task.
Attributes:
Assembly File - Defines the dll that includes the custom task.
TaskName - Defines the task name, which must be qualified with the full namespace.

<Custom tag
Any custom task defined in a DLL that is specified in a <UsingTask tag. I'll go into detail in a future post as to how to create your own custom tasks.

<Copy
Task to copy file(s)

<CreateProperty
Creates a property

<CreateItem
Creates an ItemList.


http://msdn.microsoft.com/en-us/magazine/dd150090.aspx is a good article on Team Build 2008. If you haven't already figured it out, I'm using Team Build 2008.

This pretty much covers all I care about.

TODO: detail about custom tasks.
TODO: supply nice template Build Projects--One for a ClickOnce application, and one for a web application. I've still got a bit of refining on my current project file in that my task to copy the files from the drop folder to the web cluster for final deployment copies too much and clutters things up (it doesn't really hurt anything, but it's really not right). So I've got to take a little to configure an "Exclude" attribute correctly. Once I've got that done, I should be able to set up a template file.

Russ

Tuesday, June 23, 2009

ClickOnce, Team Systems, and Versioning

You'd think everyone has only one deployment. In my last post, I covered how to set up a Team Build Type for a ClickOnce Application in Team Systems that allows you to modify the app.config file for multiple deployments. However, I had set up the versioning so that Major, minor, and build were stored in the Build Type project file, and the revision came out of the changeset.

Unfortunately, what I discovered was that I'd have to modify the project files for all deployements when the version for Major, minor, or build changed. Since I already had 20 deployments, with the number expected to reach nearly 100, this was unacceptable. I needed a way to store version information in a single file accessible from the build.

The best solution for me is to store it in the AssemblyInfo.cs file of the ClickOnce project. This data is easily modifiable from Visual Studio in the Project's properties, plus ensured the ClickOnce manifest version matched the assembly.

Again Googling, everything was backward. Plenty of links on how to set up a dynamic AssemblyInfo.cs file, or to store version data in a modifyable file as part of the project--no, no, no! This is not what I want! When I run 20 builds, I want all 20 to have the same version number, without having to modify 20 files to do so. Generating the AssemblyInfo.cs file was completely backward and would not ensure consistent version numbers. Using a shared version file was the next best thing, but I'd have to remember to modify it manually. I couldn't have the build change it as there would be a different build number for each of the 20 deployments.

Storing it in the AssemblyInfo.cs file was best as I could easily remember to always manually modify it. It would be even better if I could somehow replace the revision with the changeset number (which the project file already gets anyhow), but I can live without it.

--I can't believe how painful it is to find the information you need--

I thought of XmlRead from MSBuild.Community.Tasks (from http://msbuildtasks.tigris.org/), but AssemblyInfo.cs is in C#, not in XML. There was also a RegexMatch task that could be a possibility, but working with Regex is always nasty. Kind of makes me feel like I'm programming in Malbolge whenever I see it.

Okay, a RegEx tester I had came up with this pattern for extracting Major, minor, and build from AssemblyInfo.cs:(\[assembly\: AssemblyVersion\(.[0-9]\.[0-9]\.[0-9])?[0-9]\.[0-9]\.[0-9]

Unfortunately, after much painful work and finally looking at the source code from msbuildtasks.tigris.org, I discovered that the RegexMatch didn't return the capture match from the Regex expression, but the entire line that the Regex expression found a match on. Useless!

So I finally gave in and created my own custom task--which apparently I should have done in the first place and saved myself the headache of trusting others.

I won't go into the code for the custom task--I'll save something like that for another post. But basically it returned all captures found on a string with a Regex expression.

I used the ReadLinesFromFile task from the Microsoft.Sdc.Tasks to import the AssemblyInfo.cs file into an ItemGroup, which I converted to a PropertyGroup with a CreateProperty task, then passed this property to my custom task.

So, I had the following added:

<ReadLinesFromFile File="$(ConfigDir)/Properties/AssemblyInfo.cs">
<
Output TaskParameter="Lines"
ItemName="ItemsFromFile" />
</ReadLinesFromFile>



<!-- custom task below -->
<RegexCapture Input="@(ItemsFromFile)" Expression="(\[assembly\: AssemblyVersion\(.[0-9]\.[0-9]\.[0-9])?[0-9]\.[0-9]\.[0-9]">
<
Output ItemName ="VersionData" TaskParameter="Output" />
</
RegexCapture>

After a painful mess dealing with the confusion difference between "@" and "$", I finally had success http://msdn.microsoft.com/en-us/library/0k6kkbsd.aspx is a good MSBuild reference. http://msdn.microsoft.com/en-us/library/bb546106.aspx shows the special characters reference.

I used CreateProperty to convert the ItemList from the RegexCapture to a property, then referred to this property on the Regex that modified the version on the Landing Page, and on the ApplicationVersion of the MSBuild.

The revision was already in the project, using TfsVersion from MSBuild.Community Tasks from http://msbuildtasks.tigris.org/. I just concatenated this on the version information I grabbed from the AssemblyInfo file, thus ensuring each build would use an incrementing revision.

My next post: put this and my previous post together in one simplifed post, with details of what everything means.


Friday, June 19, 2009

ClickOnce and Team Systems Build

I'm going nuts on this one. I basically just need to build a ClickOnce application, but I need to modify the .config file as I actually need about 100 different deployments of the same application where the only variation is one setting in the .config file. The different deployments are needed for other reasons as well--which I'm not going to go into.

I've got it all working by overriding the AfterCompile target, but I'm not having success with the .config file modification. I can modify it fine, but it occurs at the wrong time or with the wrong file--the compile picks it up and creates the config.deploy without the modification. I found out the hard way that you can't modify a config.deploy file. If you do, you muck up the hash value that is stored in the manifest--the file's hash won't match the manifest's hash, and the application will refuse to install.


I Googled for help, and it seems everyone has advice, but none if it was very useful. Most of it was just restating

I found this one: http://coolthingoftheday.blogspot.com/2006/04/mageui-can-be-your-clickonce-stage-to.html, but it was pretty useless--just told me about Mage, but no info on how to use it. http://blog.gatosoft.com/PermaLink,guid,d0a0dd1e-c9ac-4fa9-a408-615454d49702.aspx provided a great sample, but it was designed to run MSbuild only from the command line, not from Team Systems, and the sample Deploy.proj has absolutely no comments to explain the meaning of various settings. It was useless of it to have a "GetVersion" target in there if I didn't understand why it was there. (It looks like it's getting the version fom one of the assemblies and then modifying the ClickOnce Landing page with it--is this assumption correct? or is there more? or am I just completely lost?)

I had also found http://www.codeproject.com/KB/install/QuickClickOnceArticle.aspx, but this was absolutely useless--I had already figured out all it's info just playing around with Visual Studio. That page is for newbies only.

http://blogs.msdn.com/echarran/archive/2006/08/09/693284.aspx seened to have some good clues which might have proved useful, but part of blog with the code got wacked due to the formatting of the website. Cut-and-paste did grab all of the code, at least. Then I compared it to the gatosoft.com link and saw all it was doing was giving another GenerateDeploymentManifest target override. Well, maybe it's something.

http://blogs.microsoft.co.il/blogs/maordavid/archive/2008/09/12/team-build-and-clickonce.aspx was useless--It pointed to someone else's blog that apparently is no longer on the Internet and showed just overriding the AfterCompile target. Yeah, okay--I alread knew that. Now get off of Google.

I didn't need to deal with the signing--I got that to work by cheating--just build the project on the build server using the tfsbuild user in Visual Studio. Once done one time, it never needs done again, and it's a lot easier than trying to figure out how to do it in Mage, or whatever.

Most of my problem is that I don't yet have a good handle on the meaning of the XML tags that are used in an MSBuild project. I don't want to waste my time in some class, and I can't find a nice website that just displays the cliff notes version. Microsoft's MSDN site goes into way too much detail to make sense of it quickly, and it isn't organized to give you a good summary of how to use it. All I want to know is how to get this up and running ASAP the way I need it to. I don't need to know all the excruciation of how to override labels.

I first started with the default .proj file that Team Systems creates when you create a new build. I tried taking pieces from that sample above, but none if it seems to execute at all. The default .proj file includes an AfterCompile target, which is completely missing from the above sample--so this led me down some wild goose chases trying to figure this out.

I finally got it down to the ClickOnce Landing page being modified and deployed correctly (default.htm), and all the code being correctly deployed--except the .config file still wasn't right. So, off to MSDN to find all the targets I can override--I need to override a target that occurs before the CoreCompile, but after all the sources are retrieved from source control. It also took me awhile to figure out that I needed a <SolutionToPublish> tag under an <ItemGroup> to get MSBuild to publish my solution. <SolutionToBuild> is for building only, which is useful for websites and non-clickonce applications (although I imagine <SolutionToPublish> will probably work for websites--my shop just always copies everything from the drop folder out to the website--well, my shop does it this way thanks to an overpriced consulting firm that claimed to know something and set us up that way--but that's another story).

http://msdn.microsoft.com/en-us/library/aa337604(VS.80).aspx demonstrated that AfterGet occurs exactly where I need it to, so I tried mucking with that.
I love the <Message> tag in these projects. I always add something like "~~" to the text so that I know it's my message and so that I can find it (as opposed to a message from MSBuild). These messages help me to know what's working and what's not.

First I have to deal with figuring out where the app.config file is locally. I'm using Xml.ModifyFile's XPath to modify my setting. This is pretty straight-foward and I might post info about it some other time.

First runthrough using AfterGet is simple: Message saying we're modifying App.config, then Xml.ModifyFile the app.config. Gotta do the source check-in dance and queue a new build, but fortunately the build only takes about 30 seconds. FAIL--app.config file not found. Okay, I had just specified "app.config" with no path--let me try to figure out where the Get drops all the sources--I just need to use $(SolutionRoot) as my base, then add the subfolders down to the project with the app.config. Now, remember to check out the .proj file before saving my changes, save my changes, check it back in, queue the build and wait 30 seconds--FAIL.

This time is some kind of compiler error:

CSC(0,0): error CS0006: Metadata file 'C:\Documents and Settings\tfsbuild\Local Settings\Temp\xxx.dll' could not be found

(xxx is the name of one of the referenced dlls).

Time to Google. Found http://msdn.microsoft.com/en-us/library/a92dycyz(VS.80).aspx. It says that the dll was not found. Oh, cute. This particular dll should have been compiled with this project. Even worse, the particular project that was failing was one I didn't even care if it didn't compile--it was a "sandbox" project--just something I set up to interactively test some of the custom controls. I have several possible solutions: 1. work my tail off trying to figure out how to overcome this goofy error, 2. work my tail off trying to figure out how to suppress the compile of this sandbox project, or 3. delete the project from source control. Being the lazy man I am, I opted for #3. I don't really need it anymore, anyhow. Zap! check-in, queue new build.

FAIL. Same compiler error, but this time with my critical ClickOnce project. The dll is project that is part of my solution, and this ClickOnce application references it--so why am I having a problem? I did check the app.config (based on the BuildLog.txt messages) and it is getting modified. It doesn't help tha this log file is over 1000 lines long.

Options: 1. Figure out why my ClickOnce project is not finding the DLL from another project in the same solution, or 2. Compile this project in Visual Studio and put the DLL on the ClickOnce project and reference it there. #2 is the lazy man's way of doing it, but only if the dll isn't going to change much. Unfortunately, most of the development is going on in this DLL (it's all the custom controls for the UI), so it's worth my effort to at least make a stab at option 1.

The log shows the dll getting built without error. The message "Skipping unpublishable project" is drawing my attention, but I'm moving on. Immediately after I see that it is building my ClickOnce application. The log shows "Project reference" and states the project for the dll. However, I see "Task 'MSBuild' skipped, due to false condition;" referencing the dll project, with a lot of convoluted logic explaining the false condition: @(NonVCProjectReference) !='' (evaluated to 'my dll project file' != '', a false condition) , but also it bypassed because it wasn't building inside VS. Entry is a wild-goose. moving on...I see "Done building target "ResolveProjectReferences" in project" with reference to my ClickOnce project. Promising--according to this the ClickOnce app should compile. Nothing to explain why the dll can't be found. The ugly thing is: it worked before I mucked with the app.config file. But mucking with the app.config file should have nothing to do with not being able to find the dll. Okay, let's get cute--I had removed the <SolutionToBuild> tag. Let's add it back in and see what happens. Good thing no one is paying attention to the Team Systems "Work items' thing--I've probably got hundreds by now.

Adding <SolutionToBuild> worked, and yeah! my config.deploy has the correct setting in it (intentionally different from what is in source control). Finally--for the install test. SUCCESS!!!

Okay to recap:

This is how to create a Build in Team Systems for a ClickOnce application that will modify a setting in the app.config file:

1. Log into the build machine as the tfsbuild user, open Visual Studio and publish the project. This handles the signing of the keys to the project. You only need to do this once for the project.

2. Define both the <SolutionToBuild> and the <SolutionToPublish> tags.

3. Define the "AfterGet" target override. You can use Xml.ModifyFile from Microsoft.Sdc.Tasks.dll, which comes from http://www.codeplex.com/sdctasks

4. Define the "AfterCompile" target override--this is where all the work really occurs. Best example for defining the "MSBuild" tag is http://blogs.microsoft.co.il/blogs/maordavid/archive/2008/09/12/team-build-and-clickonce.aspx, even though I stated it was rather useless. It does over-list parameters to use. I found that most of the values under the "Properties" attribute to be unnecessary, but at least it lists them, which could prove useful. I've included my working Team systems build project below, although I've obfusicated it. I left most of my variables in there to save myself work, though. Also, Note that I use the "Version" target override to adjust the ClickOnce version automatically, based on the change set. The "GetVersion" target is left in there from stuff I copied, and I don't really know if it does anything.


Finally, note that I don't use MAGE or MAGEUI!!

Sample TFSBuild.Proj file:


<?xml version="1.0" encoding="utf-8"?>
<!-- DO NOT EDIT the project element - the ToolsVersion specified here does not prevent the solutions
and projects in the SolutionToBuild item group from targeting other versions of the .NET framework.
-->
<Project DefaultTargets="Deploy" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="3.5">

<!-- Do not edit this -->
<Import Project="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\TeamBuild\Microsoft.TeamFoundation.Build.targets" />
<Import Project="$(MSBuildExtensionsPath)\MSBuildCommunityTasks\MSBuild.Community.Tasks.Targets"/>

<!-- Do i need this?
<Import Project="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v8.0\TeamBuild\Microsoft.TeamFoundation.Build.targets" />
<Import Project="$(MSBuildExtensionsPath)\MSBuildCommunityTasks\MSBuild.Community.Tasks.Targets"/>
-->

<UsingTask AssemblyFile="Microsoft.Sdc.Tasks.dll" TaskName="Microsoft.Sdc.Tasks.Xml.ModifyFile"/>
<UsingTask AssemblyFile="Microsoft.Sdc.Tasks.dll" TaskName="Microsoft.Sdc.Tasks.File.Replace"/>

<ProjectExtensions>

<!-- Team Foundation Build Version - DO NOT CHANGE -->
<ProjectFileVersion>2</ProjectFileVersion>

<!-- DESCRIPTION
This property is included only for backwards compatibility. The description of a build definition
is now stored in the database. For compatibility with V1 clients, keep this property in sync with
the value in the database.
-->
<Description></Description>

<!-- BUILD MACHINE
This property is included only for backwards compatibility. The default machine used for a build
definition is now stored in the database, as the MachineName property of the definition's
DefaultBuildAgent. For compatibility with V1 clients, keep this property in sync with the value
in the database.
-->
<BuildMachine>UNKNOWN</BuildMachine>

</ProjectExtensions>

<PropertyGroup>

<!-- TEAM PROJECT
This property is included only for backwards compatibility. The team project for a build
definition is now stored in the database. For compatibility with V1 clients, keep this property in
sync with the value in the database.
-->
<TeamProject>put your Team Systems Project name here</TeamProject>

<!-- BUILD DIRECTORY
This property is included only for backwards compatibility. The build directory used for a build
definition is now stored in the database, as the BuildDirectory property of the definition's
DefaultBuildAgent. For compatibility with V1 clients, keep this property in sync with the value
in the database.
-->
<BuildDirectoryPath>UNKNOWN</BuildDirectoryPath>

<!-- DROP LOCATION
This property is included only for backwards compatibility. The drop location used for a build
definition is now stored in the database. For compatibility with V1 clients, keep this property
in sync with the value in the database.
-->
<DropLocation>\\UNKNOWN\drops</DropLocation>

<!-- TESTING
Set this flag to enable/disable running tests as a post-compilation build step.
-->
<RunTest>false</RunTest>

<!-- CODE ANALYSIS
Set this property to enable/disable running code analysis. Valid values for this property are
Default, Always and Never.

Default - Perform code analysis as per the individual project settings
Always - Always perform code analysis irrespective of project settings
Never - Never perform code analysis irrespective of project settings
-->
<RunCodeAnalysis>Default</RunCodeAnalysis>

<!-- Additional Properties -->

<!-- WorkItemType
The type of the work item created on a build failure.
-->
<WorkItemType>Bug</WorkItemType>

<!-- WorkItemFieldValues
Fields and values of the work item created on a build failure.

Note: Use reference names for fields if you want the build to be resistant to field name
changes. Reference names are language independent while friendly names are changed depending
on the installed language. For example, "System.Reason" is the reference name for the "Reason"
field.
-->
<WorkItemFieldValues>System.Reason=Build Failure;System.Description=Start the build using Team Build</WorkItemFieldValues>

<!-- WorkItemTitle
Title of the work item created on build failure.
-->
<WorkItemTitle>Build failure in build:</WorkItemTitle>

<!-- DescriptionText
History comment of the work item created on a build failure.
-->
<DescriptionText>This work item was created by Team Build on a build failure.</DescriptionText>

<!-- BuildLogText
Additional comment text for the work item created on a build failure.
-->
<BuildlogText>The build log file is at:</BuildlogText>

<!-- ErrorWarningLogText
Additional comment text for the work item created on a build failure.
This text will only be added if there were errors or warnings.
-->
<ErrorWarningLogText>The errors/warnings log file is at:</ErrorWarningLogText>

<!-- UpdateAssociatedWorkItems
Set this flag to enable/disable updating associated workitems on a successful build.
-->
<UpdateAssociatedWorkItems>true</UpdateAssociatedWorkItems>

<!-- AdditionalVCOverrides
Additional text for the VCOverrides file generated for VC++ projects.
-->
<AdditionalVCOverrides></AdditionalVCOverrides>

<!-- CustomPropertiesForClean
Custom properties to pass to the MSBuild task while calling the "Clean" target for all solutions.
The format should be: PropertyName1=value1;PropertyName2=value2;...
-->
<CustomPropertiesForClean></CustomPropertiesForClean>

<!-- CustomPropertiesForBuild
Custom properties to pass to the MSBuild task while calling the default targets for all solutions.
The format should be: Property1=value1;Property2=value2;... To pass custom properties to
individual solutions, use the Properties metadata item of the SolutionToBuild ItemGroup.
-->
<CustomPropertiesForBuild></CustomPropertiesForBuild>

</PropertyGroup>

<ItemGroup>
<!-- SOLUTIONS
The paths of the solutions to build. Paths can be server paths or local paths, but server paths
relative to the location of this file are highly recommended. To add/delete solutions, edit this
ItemGroup. For example, to add a solution MySolution.sln, add the following line:

<SolutionToBuild Include="$(BuildProjectFolderPath)/path/MySolution.sln" />

To change the order in which the solutions are built, modify the order in which the solutions
appear below.

To call a target (or targets) other than the default, add a metadata item named Targets. To pass
custom properties to the solution, add a metadata item named Properties. For example, to call
the targets MyCustomTarget1 and MyCustomTarget2, passing in properties Property1 and Property2,
add the following:

<SolutionToBuild Include="$(BuildProjectFolderPath)/path/MySolution.sln">
<Targets>MyCustomTarget1;MyCustomTarget2</Targets>
<Properties>Property1=Value1;Property2=Value2</Properties>
</SolutionToBuild>
-->
<SolutionToBuild Include="$(BuildProjectFolderPath)/path/MySolution.sln">
<Targets></Targets>
<Properties></Properties>
</SolutionToBuild>

<SolutionToPublish Include="$(SolutionToBuild)" />
</ItemGroup>

<ItemGroup>
<!-- CONFIGURATIONS
The list of configurations to build. To add/delete configurations, edit this value. For example,
to add a new configuration, add the following lines:

<ConfigurationToBuild Include="Debugx86">
<FlavorToBuild>Debug</FlavorToBuild>
<PlatformToBuild>x86</PlatformToBuild>
</ConfigurationToBuild>

The Include attribute value should be unique for each ConfigurationToBuild node.
-->
<ConfigurationToBuild Include="ReleaseAny CPU">
<FlavorToBuild>Release</FlavorToBuild>
<PlatformToBuild>Any CPU</PlatformToBuild>
</ConfigurationToBuild>

</ItemGroup>

<ItemGroup>
<!-- TEST ARGUMENTS
If the RunTest property is set to true then the following test arguments will be used to run
tests. Tests can be run by specifying one or more test lists and/or one or more test containers.

To run tests using test lists, add MetaDataFile items and associated TestLists here. Paths can
be server paths or local paths, but server paths relative to the location of this file are highly
recommended:

<MetaDataFile Include="$(BuildProjectFolderPath)/HelloWorld/HelloWorld.vsmdi">
<TestList>BVT1;BVT2</TestList>
</MetaDataFile>

To run tests using test containers, add TestContainer items here:

<TestContainer Include="$(OutDir)\HelloWorldTests.dll" />
<TestContainer Include="$(SolutionRoot)\TestProject\WebTest1.webtest" />
<TestContainer Include="$(SolutionRoot)\TestProject\LoadTest1.loadtest" />

Use %2a instead of * and %3f instead of ? to prevent expansion before test assemblies are built
-->

</ItemGroup>
<PropertyGroup>
<Major>1</Major>
<Minor>1</Minor>
<Build>5</Build>
</PropertyGroup>
<PropertyGroup>
<!-- TEST ARGUMENTS
If the RunTest property is set to true, then particular tests within a
metadata file or test container may be specified here. This is
equivalent to the /test switch on mstest.exe.

<TestNames>BVT;HighPriority</TestNames>
-->

<Environment>-DEV</Environment>
<AppDescription>Your application description</AppDescription>
<SourceDir>E:\SystemVMS\DevBuildDrop\Release</SourceDir>
<PublishDir>$(SourceDir)\Publish</PublishDir>
<SupportUrl>http://whatever.com</SupportUrl>

<!-- signing certificate below -->
<SigningCert>ClickOnce_TemporaryKey.pfx</SigningCert>
<Company>My Company, Inc.</Company>

<SettingsFile>E:\SystemVMS\DevBuildDrop\settings.xml</SettingsFile>
<SolutionName>MySolution</SolutionName>

<AppConfigFile>app.config</AppConfigFile>

<CustomSetting>My setting</CustomSetting>




</PropertyGroup>

<PropertyGroup>

<!--<ConfigDir>$(SolutionRoot)\..\Binaries\Release\</ConfigDir>-->
<ConfigDir>$(SolutionRoot)\$(SolutionName)$(Environment)\$(SolutionName)\</ConfigDir>
<LocalClickOnceVirtualRootDir>ClickOncePublishing</LocalClickOnceVirtualRootDir>
<RemoteClickOnceVirtualRootDir>\\whatever\e$\Services\$(SolutionName)2\</RemoteClickOnceVirtualRootDir>
<ClickOnceUrl>http://whatever.com/$(SolutionName)/default.htm</ClickOnceUrl>
<ClickOnceApplicationUrl>$(ClickOnceUrl)$(SolutionName).application</ClickOnceApplicationUrl>

<ImageDirectory>$(SolutionRoot)\$(SolutionName)$(Environment)\Referenced Binaries</ImageDirectory>

<ClickOnceAppTitle>My ClickOnce Application</ClickOnceAppTitle>

<CurrentProdBuildFile>E:\SystemVMS\DevBuildDrop\settings.xml</CurrentProdBuildFile>

</PropertyGroup>
<ItemGroup>
<!-- ADDITIONAL REFERENCE PATH
The list of additional reference paths to use while resolving references. For example:

<AdditionalReferencePath Include="C:\MyFolder\" />
<AdditionalReferencePath Include="C:\MyFolder2\" />
-->
</ItemGroup>
<ItemGroup>
<ClickOnceInstallationFiles Include="$(SolutionName).application"/>
<ClickOnceInstallationFiles Include="$(SolutionName).exe.manifest"/>
<ClickOnceInstallationFiles Include="setup.exe"/>
<ClickOnceInstallationFiles Include="default.htm"/>
<ClickOnceLandingPage Include="$(SolutionRoot)\$(SolutionName)$(Environment)\Referenced Binaries\ClickOnceLandingPage.htm"/>

</ItemGroup>

<Target Name="Version">
<TfsVersion LocalPath="$(SolutionRoot)">
<Output TaskParameter="Changeset" PropertyName="Revision"/>
</TfsVersion>

</Target>

<Target Name="GetVersion">
<Message Text=" ~~~ Getting version info..."/>
<GetAssemblyIdentity AssemblyFiles="@(GetVersionAssembly)">
<Output TaskParameter="Assemblies"
ItemName="GetVersionAssemblyInfo"/>
</GetAssemblyIdentity>
</Target>


<Target Name="AfterGet">
<Message Text=" ~~~ Doing After Get"/>

<!-- Edit Config File here.-->
<Message Text=" ~~~ Modifying Config file for MyUserSetting: AppConfigFile=$(ConfigDir)$(AppConfigFile)"/>
<ModifyFile
Path="$(ConfigDir)$(AppConfigFile)"
XPath="/configuration/userSettings/$(SolutionName).Properties.Settings/setting[@name='MyUserSetting']/value"
NewValue="$(CustomSetting)"
Force="true" />


</Target>
<Target Name="AfterCompile" DependsOnTargets="Version">
<Message Text=" ~~~ BuildProjectFolderPath=$(BuildProjectFolderPath)"/>
<Message Text=" ~~~ SolutionRoot=$(SolutionRoot)"/>



<Message Text=" ~~~ copying @(ClickOnceLandingPage) to $(RemoteClickOnceVirtualRootDir)\default.htm"/>
<Copy SourceFiles="@(ClickOnceLandingPage)"
DestinationFiles="$(RemoteClickOnceVirtualRootDir)\default.htm"/>
<Message Text=" ~~~ Modifying landing page for app name, title, and version"/>
<!-- TODO: change to use File.RegEx -->
<ModifyFile
Path="$(RemoteClickOnceVirtualRootDir)\default.htm"
RegularExpression="#APPLICATION_NAME#"
NewValue="$(SolutionName)"
Force="true"/>
<ModifyFile
Path="$(RemoteClickOnceVirtualRootDir)\default.htm"
RegularExpression="#TITLE#"
NewValue="$(ClickOnceAppTitle)"
Force="true"/>
<ModifyFile
Path="$(RemoteClickOnceVirtualRootDir)\default.htm"
RegularExpression="#VERSION#"
NewValue="$(Major).$(Minor).$(Build).$(Revision)"
Force="true"/>
<Message Text=" ~~~ Building"/>
<MSBuild
Projects="$(SolutionRoot)/$(SolutionName)$(Environment)/$(SolutionName)/$(SolutionName).csproj"
Properties="Configuration=%(ConfigurationToBuild.FlavorToBuild);PublishDir=$(RemoteClickOnceVirtualRootDir);OutDir=$(RemoteClickOnceVirtualRootDir);ApplicationVersion=$(Major).$(Minor).$(Build).$(Revision);"
Targets="Publish" />


</Target>

</Project>

Hello World

As a programmer, I've had it with searching for technical information, finding what looks like what I want, but having inadequate information. So I spend the next week deciphering cryptic and undocumented code to tweak it to my needs.

Okay, so I'm going to post what I learn here--mainly for my own purposes, but if someone else finds it useful, then great.

Okay--Hello World. I'm here now.