Found this document:
PowerShell Provider for BizTalk 2013 this week.

Its from Francois Malgreve from Europe, and written October 2014, but most of it seems to still be applicable.

The table of contents has page number, but unfortunately, I couldn’t find any page numbers on the individual pages.

Suppose you want to list all orchestrations in an application, and then start, stop, unenlist, or enlist them.

It’s not object oriented. You don’t say $orch.unenlist, you use the command followed by the orchestration name.

There are four commandlets:
1. Enlist-Orchestartoin
2. Start-Orchestration
3. Stop-Orchestration
4. Unenlist-Orchestration

#unenlist all orchestration in a BizTalk application 
#Add-PSSnapIn -Name BiztalkFactory.PowerShell.Extensions  
#NOTE: Must be in 32-bit version of Powershell to use this SnapIn
cls 
$appName = "TL2000"
cd "Biztalk:\Applications\$appName\Orchestrations"

#list the orchestrations and some of their properties 
#(only for debug, not needed to run below)
Get-ChildItem | ft -auto 

$orchs = Get-ChildItem  #get them into a variable so we can loop 
foreach ($orch in $orchs) 
{
  Write-Host "Orch Name: $($orch.Name)" 
  Unenlist-Orchestration -Path $orch.Name 
}

Unfortunately, PowerShell and even WMI apparently don’t have access to the volume or mute functions. I found the program called NirCmd (download here) . The download is a zip, so created a directory called “c:\Program Files (x86)\NirCmd” and unzipped it there. The .chm help file has the syntax and command line options. Note, use “nircmdc.exe” the command line version to avoid any pop-up windows.

#UnMute 
& "c:\Program Files (x86)\NirCmd\nircmdc.exe" mutesysvolume 0
#Mute 
& "c:\Program Files (x86)\NirCmd\nircmdc.exe" mutesysvolume 1

Other references:
C# to do it: https://stackoverflow.com/questions/24196166/how-to-mute-an-application-with-powershell

Why mute your sound from a Powershell?

I have a laptop from the company I’m consulting for. I want schedule a job to mute it at about 6pm and un-mute it at about 8am. I don’t normally turn computers off at night, but don’t want the sounds of incoming emails and such.

Two Issues with Variable Initial Values

When doing a build of a BizTalk Orchestration, I saw this error:

illegal escape '\I'
illegal escape '\C'
illegal escape '\D'

The issue was that I had an orchestration variable that had the following ”
“inital value” (in the properties window): “e:\Integration\Config\DL.TL2000.Shipment.config.
Just like in C#, changed to add the @ sign so that blackslash would not be an escape character.

Similar Common Issue

If you put the set the initial to a string such as ‘abc’, but you don’t put in quotes around, you will get these two error:

1) identifier 'abc' dos not exist in "OrchestrationName'; are you missing an assembly reference.
2) cannot find symbol 'abc' 

Background

BizTalk let’s you create a map that uses XSLT instead of the functoids/GUI mapping. To do this, you click on the mapping grid, go to the “Grid Properties” window, and put in a filename for the “Custom XSLT Path”. I usually name it the same as the map file, just substituting the .xslt for the .btm file suffix.

In your XSLT file

Take some typical functoid map that uses a C# external library. If you look at the generated XSLT (right click the map and select “Validate”, then open the XSLT file_), you will see something like this:

xmlns:ScriptNS0="http://schemas.microsoft.com/BizTalk/2003/ScriptNS0"


Actually there will be one of these for each class that you reference.

The actual call to the c# may look like this:

   <xsl:value-of select="ScriptNS0:XMLDateToTimeHHMMSS($parmDateTime)"

In a normal map, it may be hard to figure out which ScriptNS# correlates to which class/library/.DLL. (Of course you have to reference that .DLL in your References for that project.)

Create your own Custom Extension File

This is where the magic happens! This file ties together your .DLL/classname to the ScriptNS# namespace to be used in your custom XSLT code.

Note: You don’t have to use ScriptNS0. IN the example below, I used ScriptDateFunctions, since the class name is DateFunctions. It doesn’t even have to have the word “Script” in it, but I leave that there to make sure it’s more clear.

<ExtensionObjects>  
   <ExtensionObject  
      Namespace="http://schemas.microsoft.com/BizTalk/2003/ScriptDateFunctions"  
      AssemblyName="ABC.Common.Helpers, Version=1.0.0.0, Culture=neutral, PublicKeyToken=a6349ee3fd01c3ec"  
      ClassName="ABC.Common.Helpers.DateFunctions" />  
</ExtensionObjects>  

You can get the AssemblyName, Version, Cultural and Public Key Token from the GAC (Global Assembly Cache). Navigate to your the folder:
c:\windows\microsoft.net\assembly\GAC_MSIL\ABC.Common.Helpers\

Repeat the “ExtensionObject” for each className. Note that if your .DLL has 4 classes, then you would need 4 ExtensionObjects (if you want to call all methods in all 4 classes).

I usually save this as a file called CustomExtension.xml. You could have one for each map, or one shared across all your maps.

Setup the Custom Extension for your Map

Just as you set the “Custom XSLT Path” in a previous step, click on the map grid, go to the “Properties” window, and paste or select your filename for “Custom Extension XML”.

Back to your XSLT

In the xsl:stylesheet root element, make sure you add your namespace:

xmlns:ScriptDateFunctions="http://schemas.microsoft.com/BizTalk/2003/ScriptDataFunctions"

Make sure the namespace corresponds to the class that contains the method you will call. For example, my “DateFunctions” class has a method called “XMLDateToTimeHHMMSS”.

   <xsl:value-of select="ScriptDateFunctions:XMLDateToTimeHHMMSS($parmDateTime)"

Test Your Map

Provide a sample instance to test the map. (Click on the .btm file in Solution Explorer), then in the “Properties” window, paste in the name of a file for the “TestMap Input Instance”. Now right click the .btm filename in Solution Explorer, and click “Test Map”. As usual, check for any errors in the “Output” window, and CNTL-Click on the file at the bottom where it says “Output is stored in the following file…”

Possible Errors

Prefix ‘ScriptDateFunctions’ is not defined.
You didn’t include the namespace at the top of your XSLT file.

Summary: We have now used the BizTalk “Custom Extension XML” to allow you to make calls to C# from your custom XSLT maps.

I got this error when doing custom XSLT with a BizTalk map.

It was a case of “brain fog”. The proper keyword is xsl:choose, not xsl:case. Other languages call this a switch or a case. I just temporarily forgot that XSLT calls it a “choose”.

Example of what the xsl:choose looks like (from https://www.w3schools.com/xml/xsl_choose.asp

<xsl:choose>
  <xsl:when test="expression">
    ... some output ...
  </xsl:when>
  <xsl:otherwise>
    ... some output ....
  </xsl:otherwise>
</xsl:choose>

Error

error btm1044: Input validation error: Non Segment level : [3] Transaction Set Control Number Mismatch

Situation

I was doing a test map on a BizTalk map in Visual Studio.
The data was in XML format, not EDI, so I’m surprised it tried to enforce this error. I would expect that to only occur a Receive Pipeline with real EDI data.

Data

<ns0:X12_00401_204 xmlns:ns0="http://abc.com/X12/204">
	<ST>
		<ST01>204</ST01>
		<ST02>0007</ST02>
	</ST>

        <!-- middle part omitted --> 	

	<SE>
		<SE01>22</SE01>
		<SE02>0001</SE02>
	</SE>
</ns0:X12_00401_204>

Fix

In the example above, I had to set the SE02 value to the same as the ST02 value. Note also that SE01 should be the number of EDI segments, including the ST and SE segments.

NOTE: You would presumably get the same error if you drop an EDI file into a Receive Location with an EDI pipeline.

Problem

I started working with OAGIS schemas . The schemas are very complex and many have over 1000 entities (elements, attributes, sequences, etc…) inside.

Let’s say I’m doing a BizTalk map, and I want to see if the schema has a field called “RequestedDeliveryDate”. Or maybe I want to find all the dates. You can’t do that in BizTalk Schema editor. You can open the .xsd schema file itself in NotePad++ and search, but it’s hard to read, and hard to find out where you are in the hierarchy.

Solution

Write your own program to list out all the elements, attributes etc… with the hierarchy.

For now, I’m just using “Write-Host” statements, and when the script runs, I can copy/paste the output window to NotePad++. From there, I can do normal search commands, or even RegEx search commands.

I thought about making the script look for the element pattern, but to me, it was easier to do that in NotePad++.

Code

#
#  Author: Neal Walters 
#    Date: 1/17/2020 
#   Descr: Help find element/attribute names in the huge OAGIS schemas 
#


# Return the value of an attribute from a given xmlNode 
function GetXmlElementsAttributeValue($node, $AttributeName)
{
    # Try and get the node.
    #$node = Get-XmlNode -XmlDocument $XmlDocument -NodePath $ElementPath -NamespaceURI $NamespaceURI -NodeSeparatorCharacter $NodeSeparatorCharacter

    # If the node and attribute already exist, return the attribute's value, otherwise return null.
    if ($node -and $node.$AttributeName) { return $node.$AttributeName } else { return $null }
}

# Do the recursion, looping through all elements and their children 
function explodeSchema($passNode)
{
    $depthCounter = $depthCounter + 1 
    $indent = "*" * $depthCounter 

    #Write-Host "$indent explodeSchema depth=$depthCounter NodeNameAttrValue=$($passNode.Name)" 

    $nodes = $passNode.SelectNodes($xpathEl) 
    #Write-Host "nodes count=$($nodes.count)"

    
    foreach ($node in $nodes) 
    {
       $totalNodeCount = $totalNodeCount + 1 

       
       $NodeNameAttrValue    =  GetXmlElementsAttributeValue $node "name" 
       $ref                  =  GetXmlElementsAttributeValue $node "ref" 

       $showRef = "" 
       if ($ref.length -ge 1) 
       {
          $showRef = "ref=$ref "
       }
       
       if ($node.LocalName -eq "element" -or $node.LocalName -eq "attribute")
       {
           $showName = "$($node.LocalName)=$($node.Name)"
       }
       else 
       {
           $showName  = $node.LocalName
       }


       Write-host "$indent $showName $showRef  " 

       explodeSchema $node  #go recursive here 
    }
    $depthCounter = $depthCounter - 1 

}



cls
$schemaFilename = "C:\YourFolderName\YourSchemaName.xsd"

$xpathEl = "*[local-name()='element' or local-name()='complexType' or local-name()='group' or local-name()='sequence' or local-name()='attribute']"; 

[xml] $xmlDoc = Get-Content $schemaFilename 
$node = $xmlDoc.SelectSingleNode("//*")  #get us positioned on the root element 
Write-Host "Root Node:" + $node.Name 

$depthCounter = 0 
$totalNodeCount = 0 
explodeSchema $node 

Write-Host "totalNodeCount=$totalNodeCount"   #bug here, count shows zero?? 

Sample Output

Note: The number of asterisks tell you what depth you are in the hierarchy.

Root Node: + xs:schema
* element=X12_00401_204   
** complexType   
*** sequence   
**** element=ST   
***** complexType   
****** sequence   
******* element=ST01   
******* element=ST02   
******* element=ST03   
**** element=xs:element ref=B2   
**** element=xs:element ref=B2A   
**** element=xs:element ref=L11   
**** element=xs:element ref=G62 
...
* element=N2   
** complexType   
*** sequence   
**** element=N201   
**** element=N202   
* element=N3   
** complexType   
*** sequence   
**** element=N301   
**** element=N302 

What else could be done?

Ideas for the future include:
1. Showing the field type (string, int, decimal, etc…)
2. For EDI fields, could show other attributes such as N2 or R5
3. Showing min/max occurs
4. Add ability to find field containing a given lookup string (then show the parents when a match is found). For example, show all “date” elements/attributes.

Other keywords to help find this post:
1. Recursive XPath against an XSD schema
2. XSD Schema Recursion
3. Explode Schema element names using recursion
4. Extract all schema element names to a file using a script

If you’ve been around BizTalk for a while, like me, you most certainly have gotten this error many times.

The normal solutions are:
1) Make sure you schema is deployed
2) Make sure your schema is not deployed more than once
3) Set “AllowUnrecognizedMessage” to True in the XML Disassembler.
4) Make sure the file you dropped has the proper namespace and root element.

To review, in BizTalk MessageType is the TargetNamespace#RootElement.

Here was the exact message I got when I dropped my test file:

The Messaging Engine failed while executing the inbound map
 for the message coming from source URL:"e:\Integration\Inbound\CPChemNew\204\*.xml" 
 with the Message Type "http://abc.com/X12/204/CPChem2#X12_00401_204". 
 
Details:"Finding the document specification by message type "ST" failed. Verify the schema deployed properly. " 

The message type looked okay.
http://abc.com/X12/204/CPChem2#X12_00401_204

But what was this "ST" in the second message?

Solution

I was converting a regular schema to an envelope schema in order to accomplish debaching. "Body XPath" is a parm you set so that the receive XML pipeline will automatically split the message into multiple messages).

I put the "Body XPath" one element too low. It should have split on X12_00401_204, but actually split the message into an ST, and other similar segments.

Insights

Here's what I didn't understand. I'm not sure why, but after debatching, the XML Disasembler on the Receive Pipelines, decides that it needs to verify that the target schema exists as well. Thus, the error on "ST".

How I Solved It

1. I did a test map in Visual Studio and got the results of the test map.
2. And then dropped that into the appropriate Receive Location (different from the one above).
3. Since I had tracking turned on, I check the "Tracked Message Events", and saw multiple "Transmission Failed" and looking at the body of each, I see one file has as the root, one has as the root, and so on.
4. The other thing I should have noticed was that in the error, the message type should have been: "http://abc.com/X12/204/CPChem2#X12_00401_204Looping" instead of "http://abc.com/X12/204/CPChem2#X12_00401_204". That should have hinted to me that maybe the debatching was happening. X12_00401_204Looping and X12_00401_204 were so similar I didn't notice, so lesson learned here is don't assume and be exact.

The file I'm trying to build looks like this:

<ns0:X12_00401_204_LOOPING xmlns:ns0="http://abc.com/X12/204/CPChem2">
	<X12_00401_204>
           <ST> 
              etc... 

In EDI, a customer can send multiple 204s, but we wanted a single file with multiple 204s in it (too complex to explain here).

The next thing I need to do is make sure I have a scheme that matches , and I need to add a namespace to that as well. So more work to be done, but now I'm back on track. What I need is two schemas, and the Envelope schema can import and reference the other one.

What if you need to import a bunch of BizTalk vocabulary rules (.xml files) into the Biztalk BRE (Business Rule Engine)? You don’t want to do it one at time using the Business Rule Deployment Wizard. The real question, is why Microsoft didn’t provide a batch version of that tool that does all the same features, or incorporate those features into the BTTask command.

Microsoft used to provide a tool called ImportExportRuleStore.exe. A reference to this tool can be found on Tallan Blog.

When you search on Microsoft site or MSDN, you can no longer find it nor download it, although there are some .exe copies of it on the old CodePlex site (under the BiztalkBatchBuild project). However, I didn’t want to download an un-trusted .exe. Apparently Microsoft never provided the source for it.

BTDF (BizTalk Deployment Framework) surely has some way to do import rules as well, but I didn’t dig through it. If they do, it’s probably in an MSBuild file.

At least part of that utility now seems to be replaced with a tool called “Microsoft.Practices.ESB.RuleDeployer.exe” in the Microsoft ESB directory (if you have that installed).

You can specify parameters of “-v filename.xml” to import a vocabulary.

See related blog where I have a PowerShell script to batch import all vocabulary files in a directory.