HI I get the following error in my application
System.NullReferenceException.Object Reference not set to an instance of an object on the code. I don't understand why it is throwing this particular exception.
Dim namePosition As Int16
Dim stream As IO.Stream
stream = File1.PostedFile.InputStream
Dim uploadedFile(stream.Length) As Byte
stream.Read(uploadedFile, 0, stream.Length)
namePosition = File1.PostedFile.FileName.LastIndexOf("\") + 1
fileName = File1.PostedFile.FileName.Substring(namePosition)
fileType = File1.PostedFile.ContentType
Related
Frequency should be 1 hour for creating an XML file. Once created, it needs to be sent through API. It has an API key for authenciation. It needs to be connected to a database first in order to get the data for creating the xml file. I need this functionality in VB (.NET)
Dim msgList As XElement
Dim xDoc As XDocument
Dim sw As New StringWriter()
Dim list As String = ""
Dim xmitList As String = ""
Dim xmitArray As New ArrayList
Dim xmlWriter As StringWriter = New StringWriter()
Dim SQLStatement As String = ""
Dim SQLErr As String = ""
Dim SQLResult As String = ""
Dim SQLResultTable As New DataTable
'********************
' Create the XML Document (XDeclaration sets the header information, and the XElement is the first node)
xDoc = New XDocument(
New XDeclaration("1.0", "UTF-16", "yes"),
New XElement("MessageList")
)
' Get the first node (<MessageList>) so we can then add child nodes to it when getting the data to send
msgList = xDoc.FirstNode
I came this far for creating the file in XML. Before this it needs to be connected to DB to grab the data and then start the process.
I tried creating the XML file but still stuck on completing that. Still left with two major steps excluding this, connecting to DB and sending the XML file using HTTP request. I need this functionality in VB (.NET).
I am trying to pull JSON values from a URL that I am working with at the moment. I may have done something like this before but I dont know what I'm missing here.
Here is the URL - https://eu-offering.kambicdn.org/offering/v2018/888/listView/golf.json?lang=en_GB&market=GB&client_id=2&channel_id=1&ncid=1568916879040&useCombined=true
And an image for clarity of what is needed to be extracted.
I ran a test using Tinman's approach as can be found here - How to get, JSON values to Work in VBA-JSON? , but i can't even apply his function, PrintJSONAccessors(), here
Public Sub exceljson()
Dim http As Object
Set http = CreateObject("MSXML2.XMLHTTP")
http.Open "GET",
"https://eu-offering.kambicdn.org/offering/v2018/888/listView/golf.json?lang=en_GB&market=GB&client_id=2&channel_id=1&ncid=1568916879040&useCombined=true", False
http.Send
Dim results As Variant
results = BitfinexTextToArray(http.responseText)
Worksheets(1).Range("A1").Resize(UBound(results), UBound(results,2)).Value = results
MsgBox ("complete")
End Sub
Function BitfinexTextToArray(responseText As String) As Variant
Dim item As Variant, JSON As Object
Dim MaxColumns As Long
Set JSON = ParseJson(responseText)
For Each item In JSON
If item.Count > MaxColumns Then MaxColumns = item.Count
Next
Dim results As Variant
ReDim results(1 To JSON.Count, 1 To MaxColumns)
Dim c As Long, r As Long
For Each item In JSON
r = r + 1
For c = 1 To item.Count
results(r, c) = item(c)
Next
Next
BitfinexTextToArray = results
End Function
I need help with pulling the following item values from each of the JSON "event"
1. "englishName"
2. "participant"
3. "oddsFractional"
NOTE: my example uses the JsonConverter library and requires you to add a reference to the Microsoft Scripting Runtime to access the Dictionary object.
I set up a test file with JSON loaded from your URL above. After parsing the JSON data, the exercise becomes understanding how the various levels are nested and what type of data structure is being used. In your JSON, it's a mix of Collection, Array, and Dictionary in various combinations. My example below shows how you have to stack up these nested references to get the data you're looking for.
Review the information in this answer to understand how the JSON is parsed into a hierarchical data structure.
Option Explicit
Public Sub test()
Dim fileNum As Long
fileNum = FreeFile()
Dim filename As String
filename = "C:\Temp\testdata.json"
Dim jsonInput As String
Open filename For Input As #fileNum
jsonInput = Input$(LOF(fileNum), fileNum)
Close fileNum
Dim json As Object
Set json = ParseJson(jsonInput)
Debug.Print " English Name = " & json("events")(1)("event")("englishName")
Debug.Print " Participant = " & json("events")(1)("betOffers")(1)("outcomes")(2)("participant")
Debug.Print "Odds Fractional = " & json("events")(1)("betOffers")(1)("outcomes")(2)("oddsFractional")
End Sub
An even better solution will be to create an intermediate variable and then loop over the contents in an array (or collection or dictionary).
I have been supplied a certificate that is required to send a json claim to an uri that has an api for sending the claim. The line in error is: handler.ClientCertificates.Add(certificate)
The following is my code:
Public Shared Sub Main()
Dim path As String = "E:\DRDRW_Update\Web Based Billing\vendorsupplied.pfx"
Dim password As String = "Password"
Dim strGateway As String = "https://MCE/api/WebServiceClaim"
Dim collection = New X509Certificate2Collection()
collection.Import(path, password, X509KeyStorageFlags.Exportable)
Dim certificate = collection(0)
Dim PathClaim As String = "E:\Sample Claim Submission JSON.txt"
Dim fi As New IO.FileInfo(PathClaim)
Dim jsonclaim As String = IO.File.ReadAllText(fi.FullName)
System.Net.ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12
Dim handler As New WebRequestHandler()
handler.ClientCertificates.Add(certificate)
' custom certificate validation handler to ignore untrusted remote certificate
ServicePointManager.ServerCertificateValidationCallback = New RemoteCertificateValidationCallback(AddressOf ValidateServerCertificate)
Using client = New HttpClient(handler)
Dim serializedProduct = JsonConvert.SerializeObject(jsonclaim)
Dim content = New StringContent(serializedProduct, Encoding.UTF8, "application/json")
content.Headers.Add("header1", "header2") ' require header
content.Headers.Add("token", "xxxxxxx-yyyy-zzzz")
Dim result = client.PostAsync(strGateway, content).Result ' ensures task is synchronous
' deserialize the saveresultmodel from the WS response and check for claim validation errors
Dim success As Boolean = False
If result.IsSuccessStatusCode Then
Dim resultResult As String = result.Content.ReadAsStringAsync().Result
Dim claimResult = JsonConvert.DeserializeObject(resultResult)
If claimResult.Errors.Count = 0 Then
success = True
Else
' output error results to console
For Each [error] In claimResult.Errors
Console.WriteLine(JsonConvert.SerializeObject([error]))
Next [error]
End If
End If
End Using
End Sub
Public Class WebRequestHandler
Inherits HttpClientHandler
End Class
The issue is that you have declared your own class for the WebRequestHandler. This is not the same as the System.Net.Http.WebRequestHandler.
Visual Studio had a "nice" feature offering to do this for you when it discovers a missing reference with something like "Generate Class for WebRequestHandler." This usually happens when you copy/paste code from another source and the reference was not fully defined. I never take this option and find it a weird way to approach writing code.
You do need to change your code to reference the correct handler like so.
Dim handler As New System.Net.Http.WebRequestHandler
The reason you are getting an error with that is that your project now needs a reference to another assembly (dll) with that class in it. To figure this out, I used google to find the documentation page here and noted this text: Assembly:
System.Net.Http.WebRequest (in System.Net.Http.WebRequest.dll).
This tells us to add the System.Net.Http.WebRequest reference to the project in order to access this class.
I had two issues, the first was answered by #GMan80013 by putting a reference to System.Net.Http.WebRequest.dll. The other was caused by the use of self-signing certificates. The ServicePointManager.ServerCertificateValidationCallback function needs to return true to ignore the issue of certificate validation caused by self-signed certificates.
I am using lotus notes form as .html files and I am sending values to server as json using angular js. But I want to upload files also now. How can I send files to server and extract using lotus script?
Can you please help me someone?
Like the below post. But it is done in ASP.NET . I want to do the same using lotus notes.
File uploading angular js ASP .NET
index.html
<span ng-if="quests.type == '17'">
<input type="file" file-upload multiple id='{{quests.id}}'/>
</span>
<button type="button" ng-click="submitForm();">Submit</button>
The above button will trigger the below code to executed.
Angular Code to post to server
var email=document.getElementById("email").value;
var message={"requesttype": "saveForm","email": emailid,"username": username};
$http.post("http://test.com/ajaxprocess?openagent", message).success(success).error(failure);
The above mentioned agent(lotusscript) will parse the above json and save the document as shown below.
ajaxprocess Agent code
'getting document context
Set docContext = sess.DocumentContext
If docContext.hasItem("REQUEST_CONTENT") Or docContext.hasItem("REQUEST_CONTENT_000") Then
'using openNTF lotus script classes to parse document to json object
Set userDataInfo=getJSONObjectFromDocument(docContext, "")
Dim fieldsobj As New JSONArray
'getting the fields array sent as json array
Set fieldsobj=userDataInfo.GetItemValue("fields")
fieldtype=Field.mGetItemValue("type")(0)
Dim doc As NotesDocument
Dim fieldname As String
ForAll Field In fieldsobj.Items
fieldname=Field.mGetItemValue("Fieldname")(0)
Call doc.Replaceitemvalue(fieldname,Field.mGetItemValue("value")(0))
End ForAll
call doc.save(true,false)
End If
Everything works fine expect file attachments. How can I send files to server with json and save using lotus script or is there any other workaround is there?
I finally found tip and made the solution as follows to get the base64 String and convert to attachment in lotusscript.
http://www-10.lotus.com/ldd/bpmpblog.nsf/dx/creating-a-mime-email-with-attachment?opendocument&comments
Dim s As New NotesSession
Dim stream As NotesStream
Dim body As NotesMIMEEntity
Dim header As NotesMIMEHeader
Dim StringInBase64 As String
StringInBase64=getbase64() 'your base64 string
Dim db As NotesDatabase
Set db=s.Currentdatabase
Dim tempdoc As NotesDocument
Set tempdoc=db.Createdocument()
Set stream = s.CreateStream
Call stream.WriteText(StringInBase64)
Set body = tempdoc.CreateMIMEEntity
Set header = body.createHeader("content-disposition")
Call header.setHeaderVal({attachment;filename="Onchange.xlsx"}) ' file name and type should be configurable
Call body.SetContentFromText(stream, "", ENC_BASE64)
Call stream.Close
tempdoc.form="Attachment"
Call tempdoc.save(True,False)
This works as expected. Thanks all for time you spent.
Here is the code for Multiple attachments, enhancement from Vijayakumar.
Dim session As New NotesSession
Dim db As NotesDatabase
Dim doc As NotesDocument
Set db = session.CurrentDatabase
Set doc = db.CreateDocument
Dim s As New NotesSession
Dim stream As NotesStream
Dim body As NotesMIMEEntity
Dim child As NotesMimeEntity
Dim header As NotesMIMEHeader
Set body = doc.CreateMIMEEntity
topString = Split(BASE64, ",")
Dim tmp_array() As String
i = 0
For i = 0 To Ubound(topString)
Redim Preserve tmp_array(i)
tmp_array(i) = topString(i)
Set child = body.CreateChildEntity()
Set header = child.CreateHeader("Content-Type")
Call header.SetHeaderVal("multipart/mixed")
Set header =child.createHeader("Content-Disposition")
Call header.setHeaderVal({attachment; filename=test} &Cstr(i)& {.jpg}) 'file name and type should be configure
Set header =child.CreateHeader("Content-ID")
Call header.SetHeaderVal("test" &Cstr(i)& ".jpg")
Set stream = s.CreateStream()
Call stream.WriteText(topString(i))
Call child.SetContentFromText(stream, "", ENC_BASE64)
Next
doc.form="Attachment"
'doc.Attachment = tmp_array
Call doc.save(True,False)
Call stream.Close()
s.ConvertMIME = True ' Restore conversion
We are working on a project where we need to migrate data stored in an Access database to a cache database. The Access database contains columns with a data type of Attachment; some of the tuples contain multiple attachments. I am able to obtain the filenames of these files by using .FileName, but I'm unsure how to determine when one file ends and another starts in .FileData.
I am using the following to obtain this data:
System.Data.OleDb.OleDbCommand command= new System.Data.OleDb.OleDbCommand();
command.CommandText = "select [Sheet1].[pdf].FileData,* from [Sheet1]";
command.Connection = conn;
System.Data.OleDb.OleDbDataReader rdr = command.ExecuteReader();
(My original answer to this question was misleading. It worked okay for PDF files that were subsequently opened with Adobe Reader, but it did not always work properly for other types of files. The following is the corrected version.)
Unfortunately we cannot directly retrieve the contents of a file in an Access Attachment field using OleDb. The Access Database Engine prepends some metadata to the binary contents of the file, and that metadata is included if we retrieve the .FileData via OleDb.
To illustrate, a document named "Document1.pdf" is saved to an Attachment field using the Access UI. The beginning of that PDF file looks like this:
If we use the following code to try and extract the PDF file to disk
using (OleDbCommand cmd = new OleDbCommand())
{
cmd.Connection = con;
cmd.CommandText =
"SELECT Attachments.FileData " +
"FROM AttachTest " +
"WHERE Attachments.FileName='Document1.pdf'";
using (OleDbDataReader rdr = cmd.ExecuteReader())
{
rdr.Read();
byte[] fileData = (byte[])rdr[0];
using (var fs = new FileStream(
#"C:\Users\Gord\Desktop\FromFileData.pdf",
FileMode.Create, FileAccess.Write))
{
fs.Write(fileData, 0, fileData.Length);
fs.Close();
}
}
}
then the resulting file will include the metadata at the beginning of the file (20 bytes in this case)
Adobe Reader is able to open this file because it is robust enough to ignore any "junk" that may appear in the file before the '%PDF-1.4' signature. Unfortunately not all file formats and applications are so forgiving of extraneous bytes at the beginning of the file.
The only Officialâ„¢ way of extracting files from an Attachment field in Access is to use the .SaveToFile method of an ACE DAO Field2 object, like so:
// required COM reference: Microsoft Office 14.0 Access Database Engine Object Library
//
// using Microsoft.Office.Interop.Access.Dao; ...
var dbe = new DBEngine();
Database db = dbe.OpenDatabase(#"C:\Users\Public\Database1.accdb");
Recordset rstMain = db.OpenRecordset(
"SELECT Attachments FROM AttachTest WHERE ID=1",
RecordsetTypeEnum.dbOpenSnapshot);
Recordset2 rstAttach = rstMain.Fields["Attachments"].Value;
while ((!"Document1.pdf".Equals(rstAttach.Fields["FileName"].Value)) && (!rstAttach.EOF))
{
rstAttach.MoveNext();
}
if (rstAttach.EOF)
{
Console.WriteLine("Not found.");
}
else
{
Field2 fld = (Field2)rstAttach.Fields["FileData"];
fld.SaveToFile(#"C:\Users\Gord\Desktop\FromSaveToFile.pdf");
}
db.Close();
Note that if you try to use the .Value of the Field2 object you will still get the metadata at the beginning of the byte sequence; the .SaveToFile process is what strips it out.
It took me a while to piece together information to retrieve a file stored from within the attachment field so I just thought I'd share it.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Data.OleDb;
using System.IO;
using System.Diagnostics;
namespace AttachCheck
{
public partial class Form1 : Form
{
DataSet Set1 = new DataSet();
int ColId;
public Form1()
{
InitializeComponent();
OleDbConnection connect = new OleDbConnection("Provider=Microsoft.ACE.OLEDB.12.0;Data Source='db/Adb.accdb'"); //set up connection
//CL_ID is a fk so attachments can be linked to users
OleDbCommand sql = new OleDbCommand("SELECT at_ID, [at_Name].[FileData], [at_Name].[FileName], [at_Name].[FileType] FROM Attachments WHERE at_ID =1;", connect);
//adding sql to addapter to be ran
OleDbDataAdapter OleDA = new OleDbDataAdapter(sql);
//attempting to open connection
try { connect.Open(); }
catch (Exception err) { System.Console.WriteLine(err); }
OleDA.Fill(Set1); //create and fill dataset
connect.Close();for (int i = 0; i < Set1.Tables[0].Rows.Count; i++)
{
System.Console.WriteLine(Set1.Tables[0].Rows[i]["at_Name.FileName"].ToString() + "This is the file name");
// by using a datagrid it allows you to display the attachments and select which to open, the open should be a button.
dataGridView1.Rows.Add(new object[] { Set1.Tables[0].Rows[i]["at_ID"].ToString(), Set1.Tables[0].Rows[i]["at_Name.FileName"].ToString(), "Open" });
}
}
private void dataGridView1_CellContentClick(object sender, DataGridViewCellEventArgs e)
{
DataGridViewCell cell = (DataGridViewCell)
dataGridView1.Rows[e.RowIndex].Cells[e.ColumnIndex];
System.Console.WriteLine(dataGridView1.Rows[e.RowIndex].Cells[e.ColumnIndex]);
string FullRow = dataGridView1.Rows[e.RowIndex].ToString(); //data retrieved from click on datagrid
//need to sub string to cut away row index and leave number
string SubRow = FullRow.Substring(24, 1); //cutting string down from position 24 for 1 character
System.Console.WriteLine(SubRow + " This is Row"); //
int RowId = int.Parse(SubRow); //turn row number from string into integer that can be used
string FullRow2 = dataGridView1.Rows[e.RowIndex].Cells[e.ColumnIndex].ToString(); //data retrieved from click on datagrid
//need to sub string to cut away row index and leave number
string SubRow2 = FullRow2.Substring(37, 1); //cutting string down from position 24 for 1 character
System.Console.WriteLine(SubRow2 + " This is Column"); //
int ColId = int.Parse(SubRow2); //turn row number from string into integer that can be used
if (ColId == 2)
{
string fileName = Set1.Tables[0].Rows[RowId]["at_Name.FileName"].ToString(); //assign the file to variable
//retrieving the file contents from the database as an array of bytes
byte[] fileContents = (byte[])Set1.Tables[0].Rows[RowId]["at_Name.FileData"];
fileContents = GetFileContents(fileContents); //send filecontents array to be decrypted
string fileType = Set1.Tables[0].Rows[RowId]["at_Name.FileType"].ToString();
DisplayTempFile(fileName, fileContents, fileType); //forward the file type to display file contents
}
}
private const int CONTENT_START_INDEX_DATA_OFFSET = 0; //values used for decoding
private const int UNKNOWN_DATA_OFFSET = 4; //the files
private const int EXTENSION_LENGTH_DATA_OFFSET = 8; //storedw within the access database
private const int EXTENSION_DATA_OFFSET = 12; //and this one
private byte[] GetFileContents(byte[] fileContents)
{
int contentStartIndex = BitConverter.ToInt32(fileContents, CONTENT_START_INDEX_DATA_OFFSET);
//'The next four bytes represent a value whose meaning is unknown at this stage, although it may represent a Boolean value indicating whether the data is compressed or not.
int unknown = BitConverter.ToInt32(fileContents, UNKNOWN_DATA_OFFSET);
//'The next four bytes contain the the length, in characters, of the file extension.
int extensionLength = BitConverter.ToInt32(fileContents, EXTENSION_LENGTH_DATA_OFFSET);
//'The next field in the header is the file extension, not including a dot but including a null terminator.
//'Characters are Unicode so double the character count to get the byte count.
string extension = Encoding.Unicode.GetString(fileContents, EXTENSION_DATA_OFFSET, extensionLength * 2);
return fileContents.Skip(contentStartIndex).ToArray();
}
private void DisplayTempFile(string fileName, byte[] fileContents, string fileType)
{
// System.Console.WriteLine(fileName + "File Name");
// System.Console.WriteLine(fileType + "File Type");
// System.Console.WriteLine(fileContents + "File Contents");
string tempFolderPath = Path.GetTempPath(); //creating a temperary path for file to be opened from
string tempFilePath = Path.Combine(tempFolderPath, fileName); // assigning the file to the path
if (!string.IsNullOrEmpty(tempFilePath)) //checking the temp file exists
{
tempFilePath = Path.Combine(tempFolderPath, //combines the strings 0 and 1 below
String.Format("{0}{1}",
Path.GetFileNameWithoutExtension(fileName), //0
Path.GetExtension(fileName))); //1
}
//System.Console.WriteLine(tempFolderPath + " tempFolderPath");
//System.Console.WriteLine(tempFilePath + " tempFilePath");
//'Save the file and open it.
File.WriteAllBytes(tempFilePath, fileContents);
//creates new file, writes bytes array to it then closes the file
//File.ReadAllBytes(tempFilePath);
//'Open the file.
System.Diagnostics.Process attachmentProcess = Process.Start(tempFilePath);
//chooses the program to open the file if available on the computer
}
}
}
Hope this helps someone
The following code goes through all the records of the Microsoft Access database data table and assigns each row to a recordset. The goes through all the attachments which are saved in field "Docs". Then extracts and saves those files on the disk.
This code is an extension on the code introduced by "Gord Thompson" above.
The only thing that I did was that I wrote the code for Visual Basic.NET.
Imports Microsoft.Office.Interop.Access.Dao
Put a reference to Dao by using the above line of code.
'Visual Basic.NET
Private Sub ReadAttachmentFiles()
'required COM reference: Microsoft Office 14.0 Access Database Engine Object Library
'define a new database engine and a new database
Dim dbe = New DBEngine
Dim db As Database = dbe.OpenDatabase("C:\Users\Meisam\Documents\Databases\myDatabase.accdb")
'define the main recordset object for each row
Dim rstMain As Recordset = db.OpenRecordset( _
"SELECT * FROM Companies", _
RecordsetTypeEnum.dbOpenSnapshot)
'evaluate whether the recordset is empty of records
If Not (rstMain.BOF And rstMain.EOF) Then
'if not empty, then move to the first record
rstMain.MoveFirst()
'do until the end of recordset is not reached
Do Until rstMain.EOF
Dim myID As Integer = -1
' ID is the name of primary field with uniqe values field
myID = CInt(rstMain.Fields("ID").Value)
'define the secondary recordset object for the attachment field "Docs"
Dim rstAttach As Recordset2 = rstMain.Fields("Docs").Value
'evaluate whether the recordset is empty of records
If Not (rstAttach.BOF And rstAttach.EOF) Then
'if not empty, then move to the first record
rstAttach.MoveFirst()
'do until the end of recordset is not reached
Do Until rstAttach.EOF
'get the filename for each attachment in the field "Docs"
Dim fileName As String = rstAttach.Fields("FileName").Value
Dim fld As Field2 = rstAttach.Fields("FileData")
fld.SaveToFile("C:\Users\Meisam\Documents\test\" & myID & "_" & fileName)
rstAttach.MoveNext()
Loop
End If
rstMain.MoveNext()
Loop
End If
'close the database
db.Close()
End Sub
According to Gord Thompson's answer I would like to provide the following information.
The first byte is the hexadecimal representation of the metadata's length. Byte 8 (0x04) is the hexadecimal representation of the length of the extension + 1. In this example this means that we need to remove the first 20 bytes (0x14):
This can be achieved very easily with the following functions:
Function SaveBinaryData(sFileName As String, ByteArray() As Byte)
Dim stream As New ADODB.stream 'Create Stream object
With stream
.type = adTypeBinary 'Specify stream type - we want To save binary data.
.Open 'Open the stream And write binary data To the object
.Write ByteArray
.SaveToFile sFileName, adSaveCreateOverWrite 'Save binary data To disk
End With
End Function
Public Function ReadBinaryData(sFileName As String) As Byte()
Dim stream As New ADODB.stream
With stream
.type = adTypeBinary
.Open
.LoadFromFile sFileName
ReadBinaryData = .Read
End With
End Function
Public Function ShiftL(arrBytes() As Byte, iShift As Integer) As Byte()
Dim i As Integer
Dim arrReturn() As Byte
For i = 0 To iShift - 1
ReDim Preserve arrReturn(i)
arrReturn(i) = Shift(arrBytes)
Next
ShiftL = arrReturn
End Function
Public Function Shift(arrBytes() As Byte) As Byte
Dim b As Long
If Not IsArray(arrBytes) Then
Err.Raise 13, , "Type Mismatch"
Exit Function
End If
Shift = arrBytes(0)
For b = 1 To UBound(arrBytes)
arrBytes(b - 1) = arrBytes(b)
Next b
ReDim Preserve arrBytes(UBound(arrBytes) - 1)
End Function
When you are accessing the value of the attachment field, just left shift the byte array by CDec(.Fields("FileData")(0)). After the shift you can process the file data as desired, e.g.:
Dim fldAttachment As DAO.Field2
Dim arrBytes() As Byte
Set fldAttachment = .Fields("FileData")
With fldAttachment
arrBytes = fldAttachment.value
ShiftL arrBytes, CDec(arrBytes(0))
SaveBinaryData .Fields("FileName").value, ByteArray
End With