1

I have been trying to replicate the following C++ function in my C# application but I am getting an AccessViolationException at the time the method "ObjSearchObject2" is invoked. Pretty sure I am doing a lot of things wrong. The C++ function and the corresponding headers looks like this,

bool DocumentSearch(char *pFileId)
{
    const int NUM_CONDITIONS = 1;
    const int NUM_TYPE_DEFNS = 1;

    ObjSearchObjectParm rSearchObject;
    ObjSearchCondition rSearchCondition;
    ObjObjectResults rSearchResults;

    char *pTypeDefnIdArray[NUM_TYPE_DEFNS];
    bool bReturnValue = false;

    memset(&rSearchObject, 0, sizeof(ObjSearchObjectParm));
    memset(&rSearchCondition, 0, sizeof(ObjSearchCondition));
    memset(&rSearchResults, 0, sizeof(ObjObjectResults));

    pTypeDefnIdArray[0] = "dotdA9";//Documents

    rSearchObject.obj_defn_ids = pTypeDefnIdArray;
    rSearchObject.obj_defn_count = NUM_TYPE_DEFNS;
    rSearchObject.num_conditions = NUM_CONDITIONS;
    rSearchObject.max_results = 5000;
    rSearchObject.search_cond = &rSearchCondition;

    rSearchCondition.field = "ancestor";
    rSearchCondition.op = "is";
    rSearchCondition.value = pFileId;

    if (ObjSearchObject2(&rSearchObject, &rSearchResults))
    {
        printf("ERROR: ObjSearchObject2 returned: %s \n", ObjGetErrorMsg());
    }
    else
    {
        printf("INFO : Number of returned results = %d \n", rSearchResults.num_results);

        if (rSearchResults.num_results > 0)
        {
            for (int iIndex = 0; iIndex < rSearchResults.num_results; iIndex++)
            {
                if (rSearchResults.object_handle[iIndex])
                {
                    printf("INFO : Object Id returned: %s (name=%s, updated=%s, type=%s, state=%s) \n",
                        ObjGetObjectAttr(rSearchResults.object_handle[iIndex], "id_object"),
                        ObjGetObjectAttr(rSearchResults.object_handle[iIndex], "name"),
                        ObjGetObjectAttr(rSearchResults.object_handle[iIndex], "date_update"),
                        ObjGetObjectAttr(rSearchResults.object_handle[iIndex], "id_type_definition"),
                        ObjGetObjectAttr(rSearchResults.object_handle[iIndex], "num_state")
                    );

                    ObjFreeObjectHdl(rSearchResults.object_handle[iIndex]);
                }
            }
            bReturnValue = true;
        }
    }
    return bReturnValue;
}

int ObjSearchObject2(ObjSearchObjectParm *, ObjObjectResults *);
char * ObjGetObjectAttr(ObjApiObjectHdl objectHdl, char *attr_name);
char * ObjGetErrorMsg();
void ObjFreeObjectHdl(ObjApiObjectHdl objectHdl);

typedef struct _ObjSearchObjectParm {
    int             obj_defn_count;     // mandatory
    char            **obj_defn_ids;     // mandatory
    char            *text_server_alias; // optional.
    char            *query_string;      // optional text search string
    ObjApiBoolean   include_deleted;    // defaults to OBJAPI_FALSE
    int             max_results;        // default = 200
    int             num_conditions;     // mandatory
    ObjSearchCondition  *search_cond;       // mandatory
    ObjApiBoolean   include_content;    // mandatory for COMPLEX searches, translated to Y/N in API
    ObjApiBoolean   include_metadata;   // mandatory for COMPLEX searches, translated to Y/N in API
} ObjSearchObjectParm;

enum    ObjApiSearchOp
{
    OBJAPI_MATCH_ALL=1,
    OBJAPI_MATCH_ANY=2
};

enum ObjApiBoolean
{
    OBJAPI_FALSE,
    OBJAPI_TRUE
};

typedef struct _ObjSearchCondition {
    ObjApiSearchOp  boolop;                 // only for complex searches
    char            *join_relation;         // only for complex searches
    int             num_opening_brackets;   // only for complex searches
    int             num_closing_brackets;   // only for complex searches
    char            *field;
    char            *op;
    char            *value;
} ObjSearchCondition;

typedef     void*   ObjApiObjectHdl;

typedef struct _ObjObjectResults {
    ObjApiObjectHdl     *object_handle;
    int                 num_results;
} ObjObjectResults;

My take on this is as follows, (UPDATE: Code updated)

public class ObjectiveNativeAPI
{
    private const string dllPath = @"objapi.dll";            

    public enum ObjApiBoolean
    {
        OBJAPI_FALSE,
        OBJAPI_TRUE
    };

    public enum ObjApiSearchOp
    {
        OBJAPI_MATCH_ALL = 1,
        OBJAPI_MATCH_ANY = 2
    };

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
    public struct ObjSearchObjectParm
    {
        public int obj_defn_count;
        public string[] obj_defn_ids;
        public string text_server_alias;
        public string query_string;
        public ObjApiBoolean include_deleted;
        public int max_results;
        public int num_conditions;
        public IntPtr search_cond;
        public ObjApiBoolean include_content;
        public ObjApiBoolean include_metadata;
    }

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
    public struct ObjSearchCondition
    {
        public ObjApiSearchOp boolop;
        public string join_relation;
        public int num_opening_brackets;
        public int num_closing_brackets;
        public string field;
        public string op;
        public string value;
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct ObjObjectResults
    {
        public IntPtr object_handle;
        public int num_results;
    }

    [DllImport(dllPath, EntryPoint = "ObjSearchObject2")]
    public static extern int ObjSearchObject2(ref ObjSearchObjectParm objSearchObjectParm, ref ObjObjectResults objObjectResults);

    [DllImport(dllPath, EntryPoint = "ObjGetObjectAttr")]
    public static extern string ObjGetObjectAttr(IntPtr object_handle, string attr_name);

    [DllImport(dllPath, EntryPoint = "ObjFreeObjectHdl")]
    public static extern void ObjFreeObjectHdl(IntPtr object_handle);
}

public void Run()
{
    ObjectiveNativeAPI.ObjSearchObjectParm rSearchObject = new ObjectiveNativeAPI.ObjSearchObjectParm();
    ObjectiveNativeAPI.ObjSearchCondition rSearchCondition = new ObjectiveNativeAPI.ObjSearchCondition();
    ObjectiveNativeAPI.ObjObjectResults rSearchResults = new ObjectiveNativeAPI.ObjObjectResults();

    rSearchCondition.field = "ancestor";
    rSearchCondition.op = "is";
    rSearchCondition.value = txtCotainerId.Text;

    rSearchObject.obj_defn_ids = new[] {"dotdA9"};
    rSearchObject.obj_defn_count = 1;
    rSearchObject.num_conditions = 1;
    rSearchObject.max_results = 5000;

    IntPtr search_cond =  Marshal.AllocCoTaskMem(Marshal.SizeOf(rSearchCondition));
    Marshal.StructureToPtr(rSearchCondition, search_cond, false);
    rSearchObject.search_cond = search_cond;

    int result = ObjectiveNativeAPI.ObjSearchObject2(ref rSearchObject, ref rSearchResults);
    MessageBox.Show(string.Format("FunctionResult: {0}", result));        

    Marshal.FreeCoTaskMem(search_cond);
}

I am a bit lost on this. I have managed to translate some other segments of this project but this is causing me grief. Any help around this would be appreciated.

ARahman
  • 41
  • 3
  • You have all struct declarations wrong. You need to qualify the string members with `[MarshalAs(UnmanagedType.LPStr)]`, the string arrays with `[MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.LPStr)]`, and you need to declare pointers to substructs as `IntPtr` because [`UnmanagedType.LPStruct` will not do it](https://stackoverflow.com/a/7928876/11683) - or as arrays, see https://stackoverflow.com/q/9735294/11683. – GSerg Sep 07 '17 at 06:57
  • @GSerg Not so for the the first two points. The default marshaling is fine. – David Heffernan Sep 07 '17 at 07:39
  • @DavidHeffernan I believe the default marshaling is `LPTStr` (as opposed to `LPStr`). However I agree it's better to put `CharSet=CharSet.Ansi` in the `[StructLayout]` and then not use `MarshalAs` on strings. – GSerg Sep 07 '17 at 08:02
  • @GSerg `CharSet=CharSet.Ansi` is the default isn't it. I would be explicit though. The main problems are the sub structs. Also the array in `_ObjObjectResults` needs attention. – David Heffernan Sep 07 '17 at 08:03
  • @DavidHeffernan You are most likely right, but while I know `LayoutKind.Sequential` [is the (language-dependent) default](https://learn.microsoft.com/en-us/dotnet/api/system.runtime.interopservices.layoutkind?view=netframework-4.7#Remarks), I can't find anything on the `CharSet` default. – GSerg Sep 07 '17 at 08:16
  • @GSerg I'm not actually sure that this is documented, or even guaranteed. Certainly it is kind on the reader of the code to be explicit. – David Heffernan Sep 07 '17 at 08:21
  • Thanks a lot guys for pointing me in the right direction. I changed the references to the nested structs as IntPtr and it worked. – ARahman Sep 08 '17 at 00:22
  • Actually scrap that.. It doesn't seem to work again :( I have updated the code to reflect some of the things that you guys have mentioned but it still doesn't seem to work – ARahman Sep 08 '17 at 02:13

0 Answers0