How to encode and to decode a file attachment Visual Basic

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

InfoPath Validate Attachment Control

To validate an Attachment control in InfoPath 2010 you can do one of the following.

Using the controls properties:
Right click on the Attachment control.
Select File Attachment Properties.
In the Validation section, select Cannot be blank.
Click Ok
Test it.

Using a Rule:
(Assume you have a Submit button)
Click on your Submit button.
In the Ribbon, click on Manage Rules.
In the Rules window, click on New.
For the Condition, select your Attachment control, then select is blank or is not blank.

is blank = no attachment
is not blank = a file is attached
 

infopath object reference not set to an instance of an object

using VSTA / codebehind i was getting a object reference not set to an instance of an object error.

turned out i fat-fingered some code.

make sure to check the fields you are referencing.

example:

bad –

Dim sAttchID As XPathNavigator = nav.SelectSingleNode(“/my:theAttachmentLibID“, Me.NamespaceManager)

good –

Dim sAttchID As XPathNavigator = nav.SelectSingleNode(“//my:theAttachmentLibID”, Me.NamespaceManager)

The Web application at SITE could not be found.

Error:

The Web application at http://site/Forms/AllItems.aspx could not be found. Verify that you have typed the URL correctly. If the URL should be serving existing content, the system administrator may need to add a new request URL mapping to the intended application.

at Microsoft.SharePoint.SPSite..ctor(SPFarm farm, Uri requestUri, Boolean contextSite, SPUserToken userToken)

at Microsoft.SharePoint.SPSite..ctor(String requestUrl)

at MOVE.FormCode.CTRL1_5_Clicked(Object sender, ClickedEventArgs e)

at Microsoft.Office.InfoPath.Internal.ButtonEventHost.OnButtonClick(DocActionEvent pEvent)

at Microsoft.Office.Interop.InfoPath.SemiTrust._ButtonEventSink_SinkHelper.OnClick(DocActionEvent pEvent)

/error

I spent the better part of week working on this error. The problem was related to my trying to use InfoPath Forms when I should have been using Browser enabled forms. Once I published the  form and uploaded into to Central Administration I was able to use Managed Code.

InfoPath Hyplerlink opens as Read Only

Recently ran into an issue where you place a hyperlink on a InfoPath form, user clicks the link, the item opens as read-only, no Edit Document option.

The fix is to do a regedit to allow for editing of the document.

example

[HKEY_CURRENT_USER\Software\Microsoft\Office\12.0\Common\Internet]
“OpenDocumentsReadWriteWhileBrowsing”=dword:00000001

in the Common\Internet folder you would create a new DWORD value OpenDocumentsReadWriteWhileBrowsing and assign it a Value of 1.

http://support.microsoft.com/kb/870853

InfoPath Loop in a Repeating Table

This will help you loop through a Repeating Table in InfoPath.  group2 is the name of my Repeating Table. sFunction is a function I use to check if a value has been updated in a SharePoint Library or List. If the value returned from the function does not match what I have in my drop down list, I update it with the function value.

 

Dim rootX As XPathNavigator = MainDataSource.CreateNavigator
Dim rowX As XPathNodeIterator = rootX.Select(“/my:myFields/my:group1/my:group2”, NamespaceManager)

While rowX.MoveNext
Dim sStatus As String = rowX.Current.SelectSingleNode(“my:field4”, NamespaceManager).Value
Dim sI As String = rowX.Current.SelectSingleNode(“my:field2”, NamespaceManager).Value
Dim sS As String = sFunction(sI)

If sStatus <> sS Then
rowX.Current.SelectSingleNode(“my:field4”, NamespaceManager).SetValue(sS)
End If
End While