Maintaining parallel 32-bit and 64-bit DNA files

Sep 24, 2013 at 7:04 AM
Edited Sep 24, 2013 at 9:25 AM
Since discovering Excel-DNA less than 2 months ago, I've built several XLL Add-Ins and a few have required parallel 32-bit and 64-bit compilations. Maintaining nearly duplicate DNA files proved to be a real pain especially when the differences tended to be merely the RuntimeVersion="v4.0" attribute. I couldn't find a solution in the discussion threads so I made an XSL template that on build copies the 32-bit DNA file to the 64-bit DNA file, increases the RuntimeVersion to v4.0 or adds RuntimeVersion="v4.0" if missing and preserves the !CDATA as best it can. I hope others can find this useful.
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl">
  <xsl:output method="xml" indent="yes" omit-xml-declaration="yes" />

  <xsl:template match="@* | node()">
    <xsl:copy>
      <xsl:apply-templates select="@* | node()" />
    </xsl:copy>
  </xsl:template>

  <!-- For the DnaLibrary (root) node if there is no RuntimeVersion attribute or v4.0 is 
  greater than the RuntimeVersion attribute, set the RuntimeVersion attribute to v4.0. -->
  <xsl:template match="DnaLibrary">
    <xsl:copy>
      <xsl:choose>
        <xsl:when test="not(@RuntimeVersion) or 4.0 > substring-after(@RuntimeVersion, 'v')">
          <xsl:attribute name="RuntimeVersion">v4.0</xsl:attribute>
          <xsl:apply-templates select="@*[name()!='RuntimeVersion'] | node()"/>
        </xsl:when>
        <xsl:otherwise>
          <xsl:apply-templates select="@* | node()"/>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:copy>
  </xsl:template>

  <!-- Whitespace only text normalizes to a length of 1. Text that normalizes to a longer 
  length contains non-whitespace. Wrap those values in !CDATA and disable output escaping. -->
  <xsl:template match="text()">
    <xsl:choose>
      <xsl:when test="string-length(normalize-space()) > 1">
        <xsl:text disable-output-escaping="yes">&lt;![CDATA[</xsl:text>
        <xsl:value-of select="." disable-output-escaping="yes" />
        <xsl:text disable-output-escaping="yes">]]&gt;</xsl:text>
      </xsl:when>
      <xsl:otherwise>
        <xsl:value-of select="." />
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>
</xsl:stylesheet>
It also helps to make the XSL transformation a part of the build at the very end of the .csproj file.
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets. -->
<Target Name="BeforeBuild">
  <XslTransformation XslInputPath="ExcelDna64.xslt" XmlInputPaths="ExcelDna.dna" OutputPaths="ExcelDna64.dna" />
</Target>
<Target Name="AfterBuild" />
Coordinator
Sep 24, 2013 at 9:44 AM
Hi Alton,

Thanks for posting that.

Another approach would be to nest the .dna file so that the bulk of your content is in a single file.
So you'd have:

32-bit .dna file where you want to target .NET 2.0:
<DnaLibrary RuntimeVersion="v2.0">
<ExternalLibrary Path="MyRealDnaFile.dna" Pack="true" />
</DnaLibrary>
64-bit .dna file where you want to target .NET 4.0:
<DnaLibrary RuntimeVersion="v4.0">
<ExternalLibrary Path="MyRealDnaFile.dna" Pack="true" />
</DnaLibrary>
The real .dna file content:
<DnaLibrary>
<ExternalLibrary Path="MyAddInCore.dll" Pack="true" />
<Reference Path="SomeMathsLib.dll" Pack="true" />
<CustomUI>
 ... Ribbon stuff
</CustomUI>
</DnaLibrary>
-Govert
Sep 24, 2013 at 9:48 AM
Edited Sep 24, 2013 at 9:55 AM
I like your way more. I didn't realize the ExternalLibrary could be used that way but now I know far more about XSLT than I ever wanted to! In theory, if the 32-bit .dna file didn't have the explicit RuntimeVersion="v2.0" declaration, the 64-bit could merely wrap the 32-bit file no?
Oct 3, 2013 at 6:53 PM
Govert,
Your suggestion for maintaining parallel files is nice. I, too have to deal with both 32-bit and 64-bit compiles of the the same XLLs.
My question is, how can I make your suggetion work if I am not packing everything into a single XLL?
Ismail
Oct 3, 2013 at 8:37 PM
The solutions work even if not packed into XLL files. Just make sure to specify which XLL / DNA file pair are 32-bit and 64-bit. In my case I made all my 64-bit DNA files wrap my 32-bit DNA files where my 32-bit DNA files don't specify the RuntimeVersion explicitly.
<DnaLibrary RuntimeVersion="v4.0">
<ExternalLibrary Path="MyDnaFile32.dna" Pack="true" />
</DnaLibrary>
Oct 8, 2013 at 7:47 PM
I am experiencing a problem with the ribbon part.
It seems like the XLL is loaded (I can see it in the addins list) but the Ribbon is not showing up. Everything else other than the ribbon (such as my custom functions) are working.

I have a 32-bit DNA file and a Root DNA file; I distribute both since they are not packed.

This is my 32-bit DNA file:
<?xml version="1.0" encoding="Windows-1252"?>
<DnaLibrary Name="MyApplication" ExplicitExports="true" RuntimeVersion="v4.0">
  <ExternalLibrary Path="MyApplication32.dll" ExplicitExports="true" LoadFromBytes="false" Pack="false" />
  <ExternalLibrary Path="RootDNA.dna" Pack="false" />
</DnaLibrary>
This is my RootDNA.dna file:
<?xml version="1.0" encoding="Windows-1252"?>
<DnaLibrary Name="MyApplication" ExplicitExports="true" RuntimeVersion="v4.0">
  <Reference Path="SomeOtherFile.DLL" Pack="false" />
  <Reference Path="ExcelApi.DLL" Pack="false" />
  <Reference Path="OfficeApi.DLL" Pack="false" />
  <Reference Path="NetOffice.DLL" Pack="false" />
  <Reference Path="VBIDEAPI.DLL" Pack="false" />
  <CustomUI>
       ... Ribbon stuff
  </CustomUI>
</DnaLibrary>
Can you tell me what I am doing wrong?

Ismail
Coordinator
Oct 8, 2013 at 8:00 PM
Edited Oct 8, 2013 at 8:00 PM
Hi Ismail,

Do the ribbon examples from the Excel-DNA distribution work for you?
If so, you can start there and gradually change it into your add-in to see where it stops working.

One reason the ribbon might not load is that there is an error in the ribbon xml (even the case of the attributes is important).

-Govert
Oct 8, 2013 at 8:08 PM
Hi Govert,
Thanks for responding so fast.
Actually the Ribbon and my addin package in general has been working quite well for some time when I have a complete DNA file for each of the 32-bit and 64-bit DNA files.
But my biggest headache has been to maintain the DNA files separately (32-bit and 64-bit) with the exact same Ribbon structure in it.
So, yes Ribbon is fine and it works Ok when I have a stand-alone 32-bit and 64-bit DNA files with Ribbon UI in each.
There must something wrong with the way I am breaking up these DNA files per your suggestion above.
Ismail
Coordinator
Oct 8, 2013 at 8:32 PM
Hi Ismail,

Ah - I understand. It might not work with the CustomUI in a separate .dna file, and not be anything you're doing wrong.

Maybe you can try to put the ExcelRibbon-derived class (or the <ExternalLibrary...> where it is imported) in the same .dna file as the <CustomUI>.

That might break the intention of your .dna file split, but could clarify the Excel-DNA limitation you're running into.

Another approach to managing the ribbon is to embed it in your own .dll as a resource, and extract and return from an override of ExcelRibbon.GetCustomUI. That way the ribbon .xml does not live in the .dna file.

-Govert
Oct 8, 2013 at 8:36 PM
I thought so too.
It seems like taking the ribbon out of the DNA and putting it into the DLL is a good idea.
Actually it used to be there before but I added it later into the DNA thinking that it was cool :)
I guess this split is now forcing me to add it back into the DLL and load it via ExcelRibbon.GetCustomUI as you suggested.
Thanks a lot Govert.