Part Two - A simple C# app to modify an ISM
So in my previous post I got a simple C# app written that modifies the ISM.
Alright, now to put this into the nAnt script. I'm going to assume a basic familiarity with nAnt here... Note: I'm using nAnt .085 build.
Of course, start with a <target> that will contain the new task definintion. Inside the target start a <script> task. The language we are writing in is C#.
<target name="customtask">
<script language="C#" >
Now the first thing we must do is provide all the references so that our code can interact with the InstallShield Standalone Automation. These are going to be all the same references that we saw added in Visual Studio. For my task this is the list:
<references>
<include name="C:\program files\nant\bin\interop.isappserviceslib.dll" />
<include name="C:\program files\nant\bin\interop.ismautolib.dll" />
<include name="C:\program files\nant\bin\interop.ismmupdaterlib.dll" />
<include name="C:\program files\nant\bin\interop.isupgradelib.dll" />
<include name="C:\program files\nant\bin\interop.iswibuildlib.dll" />
<include name="C:\program files\nant\bin\interop.saauto12.dll" />
<include name="C:\program files\nant\bin\interop.vba.dll" />
</references>
When we wrote the C# console application in the previous blog, the app worked because it created all the above DLLs to allow the C# managed code to be able to talk with the COM interface. These dlls were all on disk and accessible to your C# console app when it ran (as I understand it). So we need to accomplish the same thing for our C# nAnt task -- it needs to have access to all these same DLLs. In my case, I copied these DLLs to my nAnt program directory so that they would be available anytime this nAnt script runs. You might want to put yours in a different location -- as long as they are reliably going to be in that location when your script runs.
The next item is the <imports> section. The imports section corresponds to the "using" statement that we added in the original source code. So again, it is optional, but adding it allows us to shorten our code a bit since we can leave off the "SAAuto12" each time.
<imports>
<import namespace="SAAuto12"/>
</imports>
Now we get to the code itself: The <code> section. I'm sure somebody could explain why, but all I know is that your code section will all be inserted between some sort of CDATA and brackets characters, like this:
<code>
<![CDATA[
--- your code goes here ---
]] >
</code>
Again, I haven't looked up the meaning of this, I just know it works. 
In order to run the code as an nAnt task, we need to format it as a task. That requires a few extra sections of code from what we wrote previously. As a reminder, the original code was this:
ISWiProject project = new ISWiProject();
project.OpenProject(@"C:\projects\myism.ism",false);
project.SaveProject();
project.CloseProject();
Our new code starts by declaring this as a new type of Task (a class we inherit from nAnt)and implementing the "ExecuteTask" method in this class. It looks like this:
<code>
<![CDATA[
[TaskName("modifyISM")]
public class modifyISM : Task
{
protected override void ExecuteTask()
{
ISWiProject project = new ISWiProject();
project.OpenProject(@"C:\projects\myism.ism",false);
project.SaveProject();
project.CloseProject();
}
}
]] >
</code>
Now.. close up your <script> and <target> sections and you have finished coding this new custom nAnt task!
</script>
</target>
Let's do a quick check and make sure our new task 'customtask' can be run without error. Now when you run the 'customtask' target itself, all you are doing is compiling the C# code -- it does NOT actually execute it.
Go to a command prompt and run your nAnt script calling the customtask target. For example I'd go to the dir where my nAnt script is located and run: nant customtask.
You should see a portion of the output that looks like this:
customtask:
[script] Scanning assembly "cytjfj4d" for extensions.
BUILD SUCCEEDED
This is good! This means that it was able to successfully compile the code and create an assembly out of it. The assembly name is a randomly generated name and will be different every time. If you get an error -- scroll up through the build output and locate the first error. It should give you some sort of hint as to what needs fixing -- and the error should be very similar to what you would see if you were compiling this in the Visual Studio IDE.
Now that we know it compiles -- let's get wild and add an actual call to execute that code. 
<target name="runCustomTask" >
<modifyISM/>
</target>
Run your nAnt script again, this time calling the new target after calling the customtask. Such as, nant customtask runCustomTask.
If all has gone well your nAnt script will complete without error and you get BUILD SUCCEEDED!
Next post I'll tackle defining parameters that you want to pass to your custom task and making this custom task actually modify the ISM. The example will be calling the ImportStrings function.