unsafe public IntPtr MarshalManagedToNative(object ManagedObj) { // CONSIDER: Checking for null, checking object type // DOCUMENT: This function is not called if the return is null! // DOCUMENT: A null pointer is immediately returned to Excel, resulting in #NUM! // CONSIDER: Managing memory differently // Here we allocate and clear when the next array is returned // we might also return XLOPER and have xlFree called back. // TODO: Remove duplication - due to fixed / pointer interaction Reset(true); int rows; int columns; // those in the returned array int rowBase; int columnBase; if (rank == 1) { object[] objects = (object[])ManagedObj; rows = 1; rowBase = 0; columns = objects.Length; columnBase = objects.GetLowerBound(0); } else if (rank == 2) { object[,] objects = (object[,])ManagedObj; rows = objects.GetLength(0); rowBase = objects.GetLowerBound(0); columns = objects.GetLength(1); columnBase = objects.GetLowerBound(0); } else { throw new InvalidOperationException("Damaged XlObjectArrayMarshaler rank"); } int cbNativeStrings = 0; int numReferenceOpers = 0; int numReferences = 0; // Allocate native space int cbNative = Marshal.SizeOf(typeof(XlOper12)) + // OPER that is returned Marshal.SizeOf(typeof(XlOper12)) * (rows * columns); // Array of OPER inside the result pNative = Marshal.AllocCoTaskMem(cbNative); // Set up returned OPER XlOper12* pOper = (XlOper12*)pNative; // Excel chokes badly on empty arrays (e.g. crash in function wizard) - rather return the default erro value, #VALUE! if (rows * columns == 0) { pOper->errValue = (ushort)IntegrationMarshalHelpers.ExcelError_ExcelErrorValue; pOper->xlType = XlType12.XlTypeError; } else { pOper->xlType = XlType12.XlTypeArray; pOper->arrayValue.Rows = rows; pOper->arrayValue.Columns = columns; pOper->arrayValue.pOpers = ((XlOper12*)pNative + 1); } // This loop won't be entered in the empty-array case (rows * columns == 0) for (int i = 0; i < rows * columns; i++) { // Get the right object out of the array object obj; if (rank == 1) { obj = ((object[])ManagedObj)[columnBase + i]; } else { int row = i / columns; int column = i % columns; obj = ((object[,])ManagedObj)[rowBase + row, columnBase + column]; } // Get the right pOper pOper = (XlOper12*)pNative + i + 1; // Set up the oper from the object if (obj is double) { pOper->numValue = (double)obj; pOper->xlType = XlType12.XlTypeNumber; } else if (obj is string) { // We count all of the string lengths, string str = (string)obj; cbNativeStrings += (Marshal.SizeOf(typeof(XlString12)) + ((Math.Min(str.Length, XlString12.MaxLength) - 1) /* 1 char already in XlString */) * 2 /* 2 bytes per char */); // mark the Oper as a string, and // later allocate memory and return to fix pointers pOper->xlType = XlType12.XlTypeString; } else if (obj is DateTime) { pOper->numValue = ((DateTime)obj).ToOADate(); pOper->xlType = XlType12.XlTypeNumber; } else if (IntegrationMarshalHelpers.IsExcelErrorObject(obj)) { pOper->errValue = IntegrationMarshalHelpers.ExcelErrorGetValue(obj); pOper->xlType = XlType12.XlTypeError; } else if (IntegrationMarshalHelpers.IsExcelMissingObject(obj)) { pOper->xlType = XlType12.XlTypeMissing; } else if (IntegrationMarshalHelpers.IsExcelEmptyObject(obj)) { pOper->xlType = XlType12.XlTypeEmpty; } else if (IntegrationMarshalHelpers.IsExcelAsyncHandleNativeObject(obj)) { IntPtr handle = IntegrationMarshalHelpers.GetExcelAsyncHandleNativeHandle(obj); pOper->bigData.hData = handle; pOper->bigData.cbData = IntPtr.Size; pOper->xlType = XlType12.XlTypeBigData; } else if (obj is bool) { pOper->boolValue = (bool)obj ? 1 : 0; pOper->xlType = XlType12.XlTypeBoolean; } else if (obj is byte) { pOper->numValue = (double)((byte)obj); pOper->xlType = XlType12.XlTypeNumber; } else if (obj is sbyte) { pOper->numValue = (double)((sbyte)obj); pOper->xlType = XlType12.XlTypeNumber; } else if (obj is short) { pOper->numValue = (double)((short)obj); pOper->xlType = XlType12.XlTypeNumber; } else if (obj is ushort) { pOper->numValue = (double)((ushort)obj); pOper->xlType = XlType12.XlTypeNumber; } else if (obj is int) { pOper->numValue = (double)((int)obj); pOper->xlType = XlType12.XlTypeNumber; } else if (obj is uint) { pOper->numValue = (double)((uint)obj); pOper->xlType = XlType12.XlTypeNumber; } else if (obj is long) { pOper->numValue = (double)((long)obj); pOper->xlType = XlType12.XlTypeNumber; } else if (obj is ulong) { pOper->numValue = (double)((ulong)obj); pOper->xlType = XlType12.XlTypeNumber; } else if (obj is decimal) { pOper->numValue = (double)((decimal)obj); pOper->xlType = XlType12.XlTypeNumber; } else if (obj is float) { pOper->numValue = (double)((float)obj); pOper->xlType = XlType12.XlTypeNumber; } else if (IntegrationMarshalHelpers.IsExcelReferenceObject(obj)) { pOper->xlType = XlType12.XlTypeReference; // First we count all of these, // later allocate memory and return to fix pointers numReferenceOpers++; numReferences += IntegrationMarshalHelpers.ExcelReferenceGetRectangleCount(obj); // ((ExcelReference)obj).InnerReferences.Count; } else if (obj is object[]) { XlObjectArray12MarshalerImpl m = new XlObjectArray12MarshalerImpl(1); nestedInstances.Add(m); XlOper12* pNested = (XlOper12*)m.MarshalManagedToNative(obj); if (pNested->xlType == XlType12.XlTypeArray) { pOper->xlType = XlType12.XlTypeArray; pOper->arrayValue.Rows = pNested->arrayValue.Rows; pOper->arrayValue.Columns = pNested->arrayValue.Columns; pOper->arrayValue.pOpers = pNested->arrayValue.pOpers; } else { // This is the case where the array passed in has 0 length. // We set to an error to at least have a valid XLOPER pOper->xlType = XlType12.XlTypeError; pOper->errValue = IntegrationMarshalHelpers.ExcelError_ExcelErrorValue; } } else if (obj is object[,]) { XlObjectArray12MarshalerImpl m = new XlObjectArray12MarshalerImpl(2); nestedInstances.Add(m); XlOper12* pNested = (XlOper12*)m.MarshalManagedToNative(obj); if (pNested->xlType == XlType12.XlTypeArray) { pOper->xlType = XlType12.XlTypeArray; pOper->arrayValue.Rows = pNested->arrayValue.Rows; pOper->arrayValue.Columns = pNested->arrayValue.Columns; pOper->arrayValue.pOpers = pNested->arrayValue.pOpers; } else { // This is the case where the array passed in has 0,0 length. // We set to an error to at least have a valid XLOPER pOper->xlType = XlType12.XlTypeError; pOper->errValue = IntegrationMarshalHelpers.ExcelError_ExcelErrorValue; } } else if (obj is double[]) { double[] doubles = (double[])obj; object[] objects = new object[doubles.Length]; Array.Copy(doubles, objects, doubles.Length); XlObjectArray12MarshalerImpl m = new XlObjectArray12MarshalerImpl(1); nestedInstances.Add(m); XlOper12* pNested = (XlOper12*)m.MarshalManagedToNative(objects); if (pNested->xlType == XlType12.XlTypeArray) { pOper->xlType = XlType12.XlTypeArray; pOper->arrayValue.Rows = pNested->arrayValue.Rows; pOper->arrayValue.Columns = pNested->arrayValue.Columns; pOper->arrayValue.pOpers = pNested->arrayValue.pOpers; } else { // This is the case where the array passed in has 0 length. // We set to an error to at least have a valid XLOPER pOper->xlType = XlType12.XlTypeError; pOper->errValue = IntegrationMarshalHelpers.ExcelError_ExcelErrorValue; } } else if (obj is double[,]) { double[,] doubles = (double[,])obj; object[,] objects = new object[doubles.GetLength(0), doubles.GetLength(1)]; Array.Copy(doubles, objects, doubles.GetLength(0) * doubles.GetLength(1)); XlObjectArray12MarshalerImpl m = new XlObjectArray12MarshalerImpl(2); nestedInstances.Add(m); XlOper12* pNested = (XlOper12*)m.MarshalManagedToNative(objects); if (pNested->xlType == XlType12.XlTypeArray) { pOper->xlType = XlType12.XlTypeArray; pOper->arrayValue.Rows = pNested->arrayValue.Rows; pOper->arrayValue.Columns = pNested->arrayValue.Columns; pOper->arrayValue.pOpers = pNested->arrayValue.pOpers; } else { // This is the case where the array passed in has 0,0 length. // We set to an error to at least have a valid XLOPER pOper->xlType = XlType12.XlTypeError; pOper->errValue = IntegrationMarshalHelpers.ExcelError_ExcelErrorValue; } } else if (obj is Missing) { pOper->xlType = XlType12.XlTypeMissing; } else if (obj == null) { // DOCUMENT: I return Empty for nulls inside the Array, // which is not consistent with what happens in other settings. // In particular not consistent with the results of the XlObjectMarshaler // (which is not called when a null is returned, // and interpreted as ExcelErrorNum in Excel) // This works well for xlSet though. pOper->xlType = XlType12.XlTypeEmpty; } else { // Default error return pOper->errValue = IntegrationMarshalHelpers.ExcelError_ExcelErrorValue; pOper->xlType = XlType12.XlTypeError; } } // end of first pass // Now handle strings if (cbNativeStrings > 0) { // Allocate room for all the strings pNativeStrings = Marshal.AllocCoTaskMem(cbNativeStrings); // Go through the Opers and set each string char* pCurrent = (char*)pNativeStrings; for (int i = 0; i < rows * columns; i++) { // Get the corresponding oper pOper = (XlOper12*)pNative + i + 1; if (pOper->xlType == XlType12.XlTypeString) { // Get the string from the managed array string str; if (rank == 1) { str = (string)((object[])ManagedObj)[i]; } else { int row = i / columns; int column = i % columns; str = (string)((object[,])ManagedObj)[rowBase + row, columnBase + column]; } XlString12* pdest = (XlString12*)pCurrent; pOper->pstrValue = pdest; ushort charCount = (ushort)Math.Min(str.Length, XlString12.MaxLength); fixed (char* psrc = str) { char* ps = psrc; char* pd = pdest->Data; for (int k = 0; k < charCount; k++) { *(pd++) = *(ps++); } } pdest->Length = charCount; // Increment pointer within allocated memory pCurrent += charCount + 1; } } } // Now handle references if (numReferenceOpers > 0) { // Allocate room for all the references int cbNativeReferences = numReferenceOpers * 4 /* sizeof ushort + packing to get to field offset */ + numReferences * Marshal.SizeOf(typeof(XlOper12.XlRectangle12)); pNativeReferences = Marshal.AllocCoTaskMem(cbNativeReferences); IntPtr pCurrent = pNativeReferences; // Go through the Opers and set each reference int refOperIndex = 0; for (int i = 0; i < rows * columns && refOperIndex < numReferenceOpers; i++) { // Get the corresponding oper pOper = (XlOper12*)pNative + i + 1; if (pOper->xlType == XlType12.XlTypeReference) { // Get the reference from the managed array object /*ExcelReference*/ r; if (rank == 1) { r = /*(ExcelReference)*/((object[])ManagedObj)[i]; } else { int row = i / columns; int column = i % columns; r = /*(ExcelReference)*/((object[,])ManagedObj)[rowBase + row, columnBase + column]; } int refCount = IntegrationMarshalHelpers.ExcelReferenceGetRectangleCount(r); // r.InnerReferences.Count int numBytes = 4 /* sizeof ushort + packing to get to field offset */ + refCount * Marshal.SizeOf(typeof(XlOper12.XlRectangle12)); IntegrationMarshalHelpers.SetExcelReference12(pOper, (XlOper12.XlMultiRef12*)pCurrent, r); // Unchecked keyword here is redundant (it's the default for C#), // but makes clear that we rely on the overflow. // Also - numBytes must be int and not long, else we get numeric promotion and a mess again! pCurrent = IntPtr.Size == 4 ? new IntPtr(unchecked(pCurrent.ToInt32() + (int)numBytes)) : new IntPtr(unchecked(pCurrent.ToInt64() + numBytes)); refOperIndex++; } } } if (!isExcel12v) { // For big allocations, ensure that Excel allows us to free the memory if (rows * columns * 16 + cbNativeStrings + numReferences * 16 > 65535) pOper->xlType |= XlType12.XlBitDLLFree; // We are done return pNative; } else { // For the Excel12v call, we need to return an array // which will contain the pointers to the Opers. int cbOperPointers = columns * Marshal.SizeOf(typeof(XlOper12*)); pOperPointers = Marshal.AllocCoTaskMem(cbOperPointers); XlOper12** pOpers = (XlOper12**)pOperPointers; for (int i = 0; i < columns; i++) { pOpers[i] = (XlOper12*)pNative + i + 1; } return pOperPointers; } }
public static XlObjectArray12MarshalerImpl GetInstance(int rank) { // rank must be 1 or 2 if (rank == 1) { if (instance1 == null) instance1 = new XlObjectArray12MarshalerImpl(1); return instance1; } else if (rank == 2) { if (instance2 == null) instance2 = new XlObjectArray12MarshalerImpl(2); return instance2; } throw new ArgumentException("Invalid instance indentifiers for XlObjectArrayMarshalerImpl"); }