2

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

Kadir Şahbaz
78.6k57 gold badges260 silver badges407 bronze badges
asked Apr 10, 2018 at 16:27
2
  • I'm confused, what is strange? That "ref byte is the first argument" or that "byte passes to method as ref"? Commented Apr 19, 2018 at 1:58
  • Strange that you should pass a 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. Commented Apr 19, 2018 at 10:01

1 Answer 1

3
+100

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 AttachToMemorymethod.

answered Apr 19, 2018 at 3:44
2
  • This is it ! The cast to IMemoryBlobStreamVariant and ExportToVariant() made it. Although in your code, no need to pre-allocate the byte[] 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 ? Commented 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 about AttachToMemory. I thought deleting byte[] data, but I couldn't be sure. Commented Apr 19, 2018 at 10:09

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.