I am trying to retreive the Attachment of a Feature using ArcObjects in C#. The data is correct, I am able to dump the attachments to the disk using SaveToFile.
I want to work in memory and not dump to the disk and re-read the bytes as this is terribly wrong.
Also, I cannot "hack" in the database and select the BLOB directly.
Here is the relevant piece of code that I have up to now and the operation AttachToMemory which I believe is the way to go but I am unable to get it working.
IEnumAttachment enumAttachment = table.AttachmentManager.GetAttachmentsByParentIDs(ids, false);
IAttachment attachment = null;
while ((attachment = enumAttachment.Next()) != null)
{
// THIS DOES NOT COPY THE DATA, DATA = 0円
byte[] data = new byte[attachment.Size];
attachment.Data.AttachToMemory(ref data[0], (uint)attachment.Size, 0);
// THIS INDEED CREATES THE PROPER FILE WITH DATA
attachment.Data.SaveToFile("C:\path");
}
After the AttachToMemory operation, the data does not contain the bytes, it is initialized to null bytes.
I have tried to work with unmanaged memory portions but then I get some very strange results (the few first bytes are non-null then the rest is null bytes) :
IntPtr pointer = Marshal.AllocHGlobal(attachment.Size);
byte first = (byte)pointer.ToPointer(); // <---- this looks very wrong
attachment.Data.AttachToMemory(ref first, (uint)attachment.Size, 0);
Marshal.Copy(pointer, data, 0, data.Length);
Marshal.FreeHGlobal(pointer);
One of my issues in understanding how it is supposed to work is that the AttachToMemory method accepts a ref byte
as first argument. This seems very strange to me.
The reference of the ArcObject is here : http://resources.esri.com/help/9.3/arcgisengine/arcobjects/esriSystem/IMemoryBlobStream_AttachToMemory.htm
1 Answer 1
It's sensible to pass data
variable by ref
to method. Attachment could be 1 GB file.
When I used the code in the question, data
consisted of just zeros. After some research, I added some code lines. Now attachments is retrieved correctly. (I pointed out added lines)
using ESRI.ArcGIS.Framework;
using ESRI.ArcGIS.Carto;
using ESRI.ArcGIS.Geodatabase;
using ESRI.ArcGIS.ArcMapUI;
using ESRI.ArcGIS.esriSystem;
using System;
namespace ArcMapAddin
{
public class Button1 : ESRI.ArcGIS.Desktop.AddIns.Button
{
// to show message
IMessageDialog messageWindow = new MessageDialogClass();
public Button1() {}
protected override void OnClick()
{
IMxDocument mxDoc = ArcMap.Application.Document as IMxDocument;
IMap map = mxDoc.FocusMap;
ILayer layer = null;
IFeatureClass featureClass = null;
IEnumLayer enumLayer = map.Layers;
layer = enumLayer.Next(); // map has one layer
featureClass = (layer as IFeatureLayer).FeatureClass;
ITableAttachments table= (ITableAttachments)featureClass;
IAttachmentManager attachmentManager = table.AttachmentManager;
ILongArray ids = new LongArrayClass();
ids.Add(1);
IEnumAttachment enumAttachment = attachmentManager.GetAttachmentsByParentIDs(ids, false);
IAttachment attachment = null;
while ((attachment = enumAttachment.Next()) != null)
{
IMemoryBlobStream dataBlob = attachment.Data;
byte[] data = new byte[dataBlob.Size];
///////////////
// ADDED LINES
object _data = null;
(dataBlob as IMemoryBlobStreamVariant).ExportToVariant(out _data);
data = (byte[])_data;
///////////////
attachment.Data.AttachToMemory(ref data[0], (uint)attachment.Size, 0);
// CONTROL
String x = BitConverter.ToString(data, 0);
messageWindow.DoModal("RESULT", x.ToString(), null, null, ArcMap.Application.hWnd);
}
}
protected override void OnUpdate()
{
Enabled = ArcMap.Application != null;
}
}
}
I didn't find much about using AttachToMemory
method.
-
This is it ! The cast to
IMemoryBlobStreamVariant
andExportToVariant()
made it. Although in your code, no need to pre-allocate thebyte[] data
as it just consumes some memory for nothing. For info, how did you find this solution, is there any doc out there that explains this ?Simon– Simon2018年04月19日 09:58:31 +00:00Commented Apr 19, 2018 at 9:58 -
The only thing that I've found useful is just the last paragraph in this link. There are some codes about
IMemoryBlobStream
, but I didn't find aboutAttachToMemory
. I thought deletingbyte[] data
, but I couldn't be sure.Kadir Şahbaz– Kadir Şahbaz2018年04月19日 10:09:33 +00:00Commented Apr 19, 2018 at 10:09
ref byte
from a cast to another object that you cannot pass itself... but this type of construct is frequent when working with low-level APIs.