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.
[MarshalAs(UnmanagedType.LPStr)], the string arrays with[MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.LPStr)], and you need to declare pointers to substructs asIntPtrbecauseUnmanagedType.LPStructwill not do it - or as arrays, see stackoverflow.com/q/9735294/11683.LPTStr(as opposed toLPStr). However I agree it's better to putCharSet=CharSet.Ansiin the[StructLayout]and then not useMarshalAson strings.CharSet=CharSet.Ansiis the default isn't it. I would be explicit though. The main problems are the sub structs. Also the array in_ObjObjectResultsneeds attention.LayoutKind.Sequentialis the (language-dependent) default, I can't find anything on theCharSetdefault.