예제 #1
0
        public static unsafe int TryExcelImpl(int xlFunction, out object result, params object[] parameters)
        {
            int xlReturn;

            // TODO: EXCEL2007 - Review for multithreaded....
            // Set up the memory to hold the result from the call
            XlOper resultOper = new XlOper();
            resultOper.xlType = XlType.XlTypeEmpty;
            XlOper* pResultOper = &resultOper;  // No need to pin for local struct

            // Special kind of ObjectArrayMarshaler for the parameters (rank 1)
            using (XlObjectArrayMarshaler paramMarshaler = new XlObjectArrayMarshaler(1, true))
            {
                XlOper** ppOperParameters = (XlOper**)paramMarshaler.MarshalManagedToNative(parameters);
                xlReturn = Excel4v(xlFunction, pResultOper, parameters.Length, ppOperParameters);
            }

            // pResultOper now holds the result of the evaluated function
            // Get ObjectMarshaler for the return value
            ICustomMarshaler m = XlObjectMarshaler.GetInstance("");
            result = m.MarshalNativeToManaged((IntPtr)pResultOper);
            // And free any memory allocated by Excel
            Excel4v(xlFree, (XlOper*)IntPtr.Zero, 1, &pResultOper);

            return xlReturn;
        }
예제 #2
0
        public unsafe 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.

            // If array is too big!?, we just truncate

            // TODO: Remove duplication - due to fixed / pointer interaction

            Reset(true);

            ushort rows;
            int rowBase;
            ushort columns; // those in the returned array
            int columnBase;
            int allColumns;	// all in the managed array
            if (rank == 1)
            {
                object[] objects = (object[])ManagedObj;

                rows = 1;
                rowBase = 0;
                allColumns = objects.Length;
                columns = (ushort)Math.Min(objects.Length, ushort.MaxValue);
                columnBase = objects.GetLowerBound(0);
            }
            else if (rank == 2)
            {
                object[,] objects = (object[,])ManagedObj;

                rows = (ushort)Math.Min(objects.GetLength(0), ushort.MaxValue);
                rowBase = objects.GetLowerBound(0);
                allColumns = objects.GetLength(1);
                columns = (ushort)Math.Min(objects.GetLength(1), ushort.MaxValue);
                columnBase = objects.GetLowerBound(1);
            }
            else
            {
                throw new InvalidOperationException("Damaged XlObjectArrayMarshaler rank");
            }

            // Some counters for the multi-pass
            int cbNativeStrings = 0;
            int numReferenceOpers = 0;
            int numReferences = 0;

            // Allocate native space
            int cbNative = Marshal.SizeOf(typeof(XlOper)) +				// OPER that is returned
                Marshal.SizeOf(typeof(XlOper)) * (rows * columns);	// Array of OPER inside the result
            pNative = Marshal.AllocCoTaskMem(cbNative);

            // Set up returned OPER
            XlOper* pOper = (XlOper*)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 = XlType.XlTypeError;
            }
            else
            {
                // Some contents to put into an array....
                pOper->xlType = XlType.XlTypeArray;
                pOper->arrayValue.Rows = rows;
                pOper->arrayValue.Columns = columns;
                pOper->arrayValue.pOpers = ((XlOper*)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 / allColumns;
                    int column = i % allColumns;
                    obj = ((object[,])ManagedObj)[rowBase + row, columnBase + column];
                }

                // Get the right pOper
                pOper = (XlOper*)pNative + i + 1;

                // Set up the oper from the object
                if (obj is double)
                {
                    pOper->numValue = (double)obj;
                    pOper->xlType = XlType.XlTypeNumber;
                }
                else if (obj is string)
                {
                    // We count all of the string lengths,
                    string str = (string)obj;
                    cbNativeStrings += Math.Min(str.Length, 255) + 1;
                    // mark the Oper as a string, and
                    // later allocate memory and return to fix pointers
                    pOper->xlType = XlType.XlTypeString;
                }
                else if (obj is DateTime)
                {
                    pOper->numValue = ((DateTime)obj).ToOADate();
                    pOper->xlType = XlType.XlTypeNumber;
                }
                else if (IntegrationMarshalHelpers.IsExcelErrorObject(obj))
                {
                    pOper->errValue = (ushort)IntegrationMarshalHelpers.ExcelErrorGetValue(obj);
                    pOper->xlType = XlType.XlTypeError;
                }
                else if (IntegrationMarshalHelpers.IsExcelMissingObject(obj))
                {
                    pOper->xlType = XlType.XlTypeMissing;
                }
                else if (IntegrationMarshalHelpers.IsExcelEmptyObject(obj))
                {
                    pOper->xlType = XlType.XlTypeEmpty;
                }
                else if (obj is bool)
                {
                    pOper->boolValue = (bool)obj ? (ushort)1 : (ushort)0;
                    pOper->xlType = XlType.XlTypeBoolean;
                }
                else if (obj is short)
                {
                    pOper->numValue = (double)((short)obj); // int16 in XlOper
                    pOper->xlType = XlType.XlTypeNumber;
                }
                else if (obj is ushort)
                {
                    pOper->numValue = (double)((ushort)obj);
                    pOper->xlType = XlType.XlTypeNumber;
                }
                else if (obj is int)
                {
                    pOper->numValue = (double)((int)obj);
                    pOper->xlType = XlType.XlTypeNumber;
                }
                else if (obj is uint)
                {
                    pOper->numValue = (double)((uint)obj);
                    pOper->xlType = XlType.XlTypeNumber;
                }
                else if (obj is long)
                {
                    pOper->numValue = (double)((long)obj);
                    pOper->xlType = XlType.XlTypeNumber;
                }
                else if (obj is ulong)
                {
                    pOper->numValue = (double)((ulong)obj);
                    pOper->xlType = XlType.XlTypeNumber;
                }
                else if (obj is decimal)
                {
                    pOper->numValue = (double)((decimal)obj);
                    pOper->xlType = XlType.XlTypeNumber;
                }
                else if (obj is float)
                {
                    pOper->numValue = (double)((float)obj);
                    pOper->xlType = XlType.XlTypeNumber;
                }
                else if (IntegrationMarshalHelpers.IsExcelReferenceObject(obj))
                {
                    pOper->xlType = XlType.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[])
                {
                    XlObjectArrayMarshaler m = new XlObjectArrayMarshaler(1);
                    nestedInstances.Add(m);
                    XlOper* pNested = (XlOper*)m.MarshalManagedToNative(obj);
                    if (pNested->xlType == XlType.XlTypeArray)
                    {
                        pOper->xlType = XlType.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 = XlType.XlTypeError;
                        pOper->errValue = (ushort)IntegrationMarshalHelpers.ExcelError_ExcelErrorValue;
                    }
                }
                else if (obj is object[,])
                {
                    XlObjectArrayMarshaler m = new XlObjectArrayMarshaler(2);
                    nestedInstances.Add(m);
                    XlOper* pNested = (XlOper*)m.MarshalManagedToNative(obj);
                    if (pNested->xlType == XlType.XlTypeArray)
                    {
                        pOper->xlType = XlType.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 = XlType.XlTypeError;
                        pOper->errValue = (ushort)IntegrationMarshalHelpers.ExcelError_ExcelErrorValue;
                    }
                }
                else if (obj is System.Reflection.Missing)
                {
                    pOper->xlType = XlType.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.
                    // CONSIDER: Create an ExcelEmpty type to allow this to be more explicit,
                    // and return ErrNum here
                    pOper->xlType = XlType.XlTypeEmpty;
                }
                else
                {
                    // Default error return
                    pOper->errValue = (ushort)IntegrationMarshalHelpers.ExcelError_ExcelErrorValue;
                    pOper->xlType = XlType.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
                byte* pCurrent = (byte*)pNativeStrings;
                for (int i = 0; i < rows * columns; i++)
                {
                    // Get the corresponding oper
                    pOper = (XlOper*)pNative + i + 1;
                    if (pOper->xlType == XlType.XlTypeString)
                    {
                        // Get the string from the managed array
                        string str;
                        if (rank == 1)
                        {
                            str = (string)((object[])ManagedObj)[i];
                        }
                        else
                        {
                            int row = i / allColumns;
                            int column = i % allColumns;
                            str = (string)((object[,])ManagedObj)[rowBase + row, columnBase + column];
                        }

                        XlString* pXlString = (XlString*)pCurrent;
                        pOper->pstrValue = pXlString;
                        int charCount = Math.Min(str.Length, 255);
                        fixed (char* psrc = str)
                        {
                            // Write the data and length to the XlString
                            // Support for system codepage by hmd
                            // int written = Encoding.ASCII.GetBytes(psrc, charCount, pXlString->Data, 255);
                            Encoding enc = Encoding.GetEncoding(ASCIIEncoding.Default.CodePage,
                                                                EncoderFallback.ReplacementFallback,
                                                                DecoderFallback.ReplacementFallback);
                            int written = enc.GetBytes(psrc, charCount, pXlString->Data, 255);
                            pXlString->Length = (byte)written;
                            // Increment pointer within allocated memory
                            pCurrent += written + 1;
                        }
                    }
                }
            }

            // Now handle references
            if (numReferenceOpers > 0)
            {
                // Allocate room for all the references
                int cbNativeReferences = numReferenceOpers * sizeof(ushort) + numReferences * Marshal.SizeOf(typeof(XlOper.XlRectangle));
                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 = (XlOper*)pNative + i + 1;
                    if (pOper->xlType == XlType.XlTypeReference)
                    {
                        // Get the reference from the managed array
                        object /*ExcelReference*/ r;
                        if (rank == 1)
                        {
                            r = /*(ExcelReference)*/((object[])ManagedObj)[i];
                        }
                        else
                        {
                            int row = i / allColumns;
                            int column = i % allColumns;
                            r = /*(ExcelReference)*/((object[,])ManagedObj)[rowBase + row, columnBase + column];
                        }

                        int refCount = IntegrationMarshalHelpers.ExcelReferenceGetRectangleCount(r); // r.InnerReferences.Count
                        int numBytes = Marshal.SizeOf(typeof(ushort)) + refCount * Marshal.SizeOf(typeof(XlOper.XlRectangle));

                        IntegrationMarshalHelpers.SetExcelReference(pOper, (XlOper.XlMultiRef*)pCurrent, r);

                        pCurrent = new IntPtr(unchecked(pCurrent.ToInt32() + (int)numBytes));
                        refOperIndex++;
                    }
                }
            }

            if (!isExcel4v)
            {
                // For big allocations, ensure that Excel allows us to free the memory
                if (rows * columns * 16 + cbNativeStrings + numReferences * 8 > 65535)
                    pOper->xlType |= XlType.XlBitDLLFree;

                // We are done
                return pNative;
            }
            else
            {
                // For the Excel4v call, we need to return an array
                // which will contain the pointers to the Opers.
                int cbOperPointers = columns * Marshal.SizeOf(typeof(XlOper*));
                pOperPointers = Marshal.AllocCoTaskMem(cbOperPointers);
                XlOper** pOpers = (XlOper**)pOperPointers;
                for (int i = 0; i < columns; i++)
                {
                    pOpers[i] = (XlOper*)pNative + i + 1;
                }
                return pOperPointers;
            }
        }
예제 #3
0
 public static ICustomMarshaler GetInstance(string marshalCookie)
 {
     // marshalCookie denotes the array rank
     // must be 1 or 2
     if (marshalCookie == "1")
     {
         if (instance1 == null)
             instance1 = new XlObjectArrayMarshaler(1);
         return instance1;
     }
     else if (marshalCookie == "2")
     {
         if (instance2 == null)
             instance2 = new XlObjectArrayMarshaler(2);
         return instance2;
     }
     throw new ArgumentException("Invalid cookie for XlObjectArrayMarshaler");
 }