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

  • I'm confused, what is strange? That "ref byte is the first argument" or that "byte passes to method as ref"? – Kadir Şahbaz Apr 19 '18 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. – Simon Apr 19 '18 at 10:01
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.

  • 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 ? – Simon Apr 19 '18 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. – Kadir Şahbaz Apr 19 '18 at 10:09

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.