Microsoft was nice enough to post a code example in C#, but I’m lazy and haven’t moved away from Visual Basic.
How to encode and to decode a file attachment programmatically by using Visual C# Visual Basic in InfoPath 2010 or in InfoPath 2007
C# example:
http://support.microsoft.com/kb/2517906
VB examples:
decode.vb
Imports System
Imports System.IO
Imports System.Text
Namespace InfoPathAttachmentEncoding
'''
<summary> ''' Decodes a file attachment and saves it to a specified path.
''' </summary>
Public Class InfoPathAttachmentDecoder
Private Const SP1Header_Size As Integer = 20
Private Const FIXED_HEADER As Integer = 16
Private fileSize As Integer
Private attachmentNameLength As Integer
Private attachmentName As String
Private m_decodedAttachment As Byte()
'''
<summary> ''' Accepts the Base64 encoded string
''' that is the attachment.
''' </summary>
Public Sub New(ByVal theBase64EncodedString As String)
Dim theData As Byte() = Convert.FromBase64String(theBase64EncodedString)
Using ms As New MemoryStream(theData)
Dim theReader As New BinaryReader(ms)
DecodeAttachment(theReader)
End Using
End Sub
Private Sub DecodeAttachment(ByVal theReader As BinaryReader)
'Position the reader to obtain the file size.
Dim headerData As Byte() = New Byte(FIXED_HEADER - 1) {}
headerData = theReader.ReadBytes(headerData.Length)
fileSize = CInt(theReader.ReadUInt32())
attachmentNameLength = CInt(theReader.ReadUInt32()) * 2
Dim fileNameBytes As Byte() = theReader.ReadBytes(attachmentNameLength)
'InfoPath uses UTF8 encoding.
Dim enc As Encoding = Encoding.Unicode
attachmentName = enc.GetString(fileNameBytes, 0, attachmentNameLength - 2)
m_decodedAttachment = theReader.ReadBytes(fileSize)
End Sub
Public Sub SaveAttachment(ByVal saveLocation As String)
Dim fullFileName As String = saveLocation
If Not fullFileName.EndsWith(Path.DirectorySeparatorChar.ToString()) Then
fullFileName += Path.DirectorySeparatorChar
End If
fullFileName += attachmentName
If File.Exists(fullFileName) Then
File.Delete(fullFileName)
End If
Dim fs As New FileStream(fullFileName, FileMode.CreateNew)
Dim bw As New BinaryWriter(fs)
bw.Write(m_decodedAttachment)
bw.Close()
fs.Close()
End Sub
Public ReadOnly Property Filename() As String
Get
Return attachmentName
End Get
End Property
Public ReadOnly Property DecodedAttachment() As Byte()
Get
Return m_decodedAttachment
End Get
End Property
End Class
End Namespace
encode.vb
Imports System
Imports System.IO
Imports System.Text
Imports System.Security.Cryptography
Imports InfoPathAttachmentEncoding
Namespace InfoPathAttachmentEncoding
'''
<summary> ''' InfoPathAttachment encodes file data into the format expected by InfoPath for use in file attachment nodes.
''' </summary>
Public Class InfoPathAttachmentEncoder
Private base64EncodedFile As String = String.Empty
Private fullyQualifiedFileName As String
'''
<summary> ''' Creates an encoder to create an InfoPath attachment string.
''' </summary>
'''
Public Sub New(ByVal fullyQualifiedFileName As String)
If fullyQualifiedFileName = String.Empty Then
Throw New ArgumentException("Must specify file name", "fullyQualifiedFileName")
End If
If Not File.Exists(fullyQualifiedFileName) Then
Throw New FileNotFoundException("File does not exist: " + fullyQualifiedFileName, fullyQualifiedFileName)
End If
Me.fullyQualifiedFileName = fullyQualifiedFileName
End Sub
'''
<summary> ''' Returns a Base64 encoded string.
''' </summary>
''' String
Public Function ToBase64String() As String
If base64EncodedFile <> String.Empty Then
Return base64EncodedFile
End If
' This memory stream will hold the InfoPath file attachment buffer before Base64 encoding.
Dim ms As New MemoryStream()
' Obtain the file information.
Using br As New BinaryReader(File.Open(fullyQualifiedFileName, FileMode.Open, FileAccess.Read, FileShare.Read))
Dim fileName As String = Path.GetFileName(fullyQualifiedFileName)
Dim fileNameLength As UInteger = CUInt(fileName.Length) + 1
Dim fileNameBytes As Byte() = Encoding.Unicode.GetBytes(fileName)
Using bw As New BinaryWriter(ms)
' Write the InfoPath attachment signature.
bw.Write(New Byte() {&HC7, &H49, &H46, &H41})
' Write the default header information.
bw.Write(CUInt(&H14))
' size
bw.Write(CUInt(&H1))
' version
bw.Write(CUInt(&H0))
' reserved
' Write the file size.
bw.Write(CUInt(br.BaseStream.Length))
' Write the size of the file name.
bw.Write(CUInt(fileNameLength))
' Write the file name (Unicode encoded).
bw.Write(fileNameBytes)
' Write the file name terminator. This is two nulls in Unicode.
bw.Write(New Byte() {0, 0})
' Iterate through the file reading data and writing it to the outbuffer.
Dim data As Byte() = New Byte(64 * 1024 - 1) {}
Dim bytesRead As Integer = 1
While bytesRead > 0
bytesRead = br.Read(data, 0, data.Length)
bw.Write(data, 0, bytesRead)
End While
End Using
End Using
' This memorystream will hold the Base64 encoded InfoPath attachment.
Dim msOut As New MemoryStream()
Using br As New BinaryReader(New MemoryStream(ms.ToArray()))
' Create a Base64 transform to do the encoding.
Dim tf As New ToBase64Transform()
Dim data As Byte() = New Byte(tf.InputBlockSize - 1) {}
Dim outData As Byte() = New Byte(tf.OutputBlockSize - 1) {}
Dim bytesRead As Integer = 1
While bytesRead > 0
bytesRead = br.Read(data, 0, data.Length)
If bytesRead = data.Length Then
tf.TransformBlock(data, 0, bytesRead, outData, 0)
Else
outData = tf.TransformFinalBlock(data, 0, bytesRead)
End If
msOut.Write(outData, 0, outData.Length)
End While
End Using
msOut.Close()
Return InlineAssignHelper(base64EncodedFile, Encoding.ASCII.GetString(msOut.ToArray()))
End Function
Private Shared Function InlineAssignHelper(Of T)(ByRef target As T, ByVal value As T) As T
target = value
Return value
End Function
End Class
End Namespace
Imports
Imports InfoPathAttachmentEncoding
btnAttach_Clicked
Public Sub btnAttach_Clicked(ByVal sender As Object, ByVal e As ClickedEventArgs)
Dim ns As XmlNamespaceManager = Me.NamespaceManager
Dim xnMain As XPathNavigator = Me.MainDataSource.CreateNavigator()
Dim xnAttNode As XPathNavigator = xnMain.SelectSingleNode("/my:myFields/my:theAttachmentField", ns)
Dim xnFileName As XPathNavigator = xnMain.SelectSingleNode("/my:myFields/my:theAttachmentName", ns)
'Obtain the text of the filename node.
Dim fileName As String = xnFileName.Value
If fileName.Length > 0 Then
'Encode the file and assign it to the attachment node.
Dim myEncoder As New InfoPathAttachmentEncoder(fileName)
'Check for the "xsi:nil" attribute on the file attachment node and remove it
'before setting the value to attach the filerRemove the "nil" attribute
If xnAttNode.MoveToAttribute("nil", "http://www.w3.org/2001/XMLSchema-instance") Then
xnAttNode.DeleteSelf()
End If
'Attach the file
xnAttNode.SetValue(myEncoder.ToBase64String())
End If
End Sub
btnSave_Clicked
Public Sub btnSave_Clicked(ByVal sender As Object, ByVal e As ClickedEventArgs)
'Create an XmlNamespaceManager
Dim ns As XmlNamespaceManager = Me.NamespaceManager
'Create an XPathNavigator object for the Main data source
Dim xnMain As XPathNavigator = Me.MainDataSource.CreateNavigator()
'Create an XPathNavigator object for the attachment node
Dim xnAttNode As XPathNavigator = xnMain.SelectSingleNode("/my:myFields/my:theAttachmentField", ns)
'Obtain the text of the node.
Dim theAttachment As String = xnAttNode.Value
If theAttachment.Length > 0 Then
Dim myDecoder As New InfoPathAttachmentDecoder(theAttachment)
myDecoder.SaveAttachment("----------ENTER THE PATH HERE-----------")
End If
End Sub