Saltar al contenido

Excel ExcelDNA C # / Intente copiar el comportamiento de Bloomberg BDH () (escribiendo Array después de una solicitud web)

Solución:

Supongo que ha probado la muestra Excel-DNA ArrayResizer, que evita con cuidado muchos de los problemas con los que se está encontrando. Me gustaría entender cuáles son las desventajas de la array-Enfoque de redacción de fórmulas.

Ahora, sobre tu función:

En primer lugar, no puede pasar de forma segura el objeto Range COM del ‘llamador’ a otro hilo, en lugar de pasar un string con la dirección y obtenga el objeto COM del otro hilo (usando una llamada a ExcelDnaUtil.Application en el hilo de trabajo). Sin embargo, la mayoría de las veces tendrás suerte. La mejor manera de hacer esto es desde el hilo de trabajo para que Excel ejecute un macro en el hilo principal, llamando a Application.Run. La muestra de Excel-DNA ArrayResizer muestra cómo se puede hacer esto.

En segundo lugar, es casi seguro que no desee ActiveCell, sino Application.Caller. Es posible que ActiveCell no tenga nada que ver con la celda desde la que se ejecuta la fórmula.

Siguiente: Excel volverá a calcular su función cada vez que configure la Fórmula nuevamente, por lo que lo pondrá en un bucle sin fin cuando habilite el conjunto de Fórmula en su cláusula final. No puede establecer tanto el valor como la fórmula para una celda – si una celda tiene una fórmula, Excel usará la fórmula para calcular el valor. Si establece el Valor, la Fórmula se elimina. No está claro qué es lo que realmente quiere dejar en el [0,0] celda: IIRC Bloomberg modifica la fórmula de una manera que le hace recordar qué tan grande se escribió un rango. Puede intentar agregar algunos parámetros a su función que le digan a su función si debe recalcular o si debe devolver un valor real como resultado.

Finalmente, es posible que desee reconsiderar si la función Bloomberg BDH es un buen ejemplo de lo que desea hacer. Rompe el cálculo de dependencia de su hoja, lo que tiene implicaciones tanto para el rendimiento como para mantener la coherencia del modelo de hoja de cálculo.

Mi problema fue:

  • escritura dinámica array

  • los datos se recuperan de forma asíncrona a través de un servicio web

Después de discutir con Govert, decidí tomar un resultado como una array y no copiar las funciones de Bloomberg (escribir un array pero devuelve un solo valor).

Finalmente, para resolver mi problema, utilicé http://excel-dna.net/2011/01/30/resizing-excel-udf-result-arrays/ y modifiqué el resize() función.

Este código no es RTD.

El siguiente código funciona en un archivo .dna


 ResizeJobs = new Queue();
        static Dictionary JobIsDone = new Dictionary();

        // This function will run in the UDF context.
        // Needs extra protection to allow multithreaded use.
        public static object Resize(object args)
        
            ExcelReference caller = XlCall.Excel(XlCall.xlfCaller) as ExcelReference;
            if (caller == null)
                return ExcelError.ExcelErrorNA;

            if (!JobIsDone.ContainsKey(GetHashcode(caller)))
            
                BackgroundWorker(caller);
                return ExcelError.ExcelErrorNA;
            
            else
            
                // Size is already OK - just return result
                object[,] array = (object[,])JobIsDone[GetHashcode(caller)];
                JobIsDone.Remove(GetHashcode(caller));
                return array;
            
        

        /// 
        /// Simulate WebServiceRequest
        /// 
        /// 
        /// 
        /// 
        static void BackgroundWorker(ExcelReference caller)
         
            BackgroundWorker bw = new BackgroundWorker();
            bw.DoWork += (sender, args) =>
            
                Thread.Sleep(3000);
            ;
            bw.RunWorkerCompleted += (sender, args) =>
            
                // La requete
                Random r = new Random();
                object[,] array = ResizeTest.MakeArray(r.Next(10), r.Next(10));

                JobIsDone[GetHashcode(caller)] = array;
                int rows = array.GetLength(0);
                int columns = array.GetLength(1);
                EnqueueResize(caller, rows, columns);
                AsyncRunMacro("DoResizing");
            ;

            bw.RunWorkerAsync();
        

        static string GetHashcode(ExcelReference caller)
        
            return caller.SheetId + ":L" + caller.RowFirst + "C" + caller.ColumnFirst;
        


        static void EnqueueResize(ExcelReference caller, int rows, int columns)
        
            ExcelReference target = new ExcelReference(caller.RowFirst, caller.RowFirst + rows - 1, caller.ColumnFirst, caller.ColumnFirst + columns - 1, caller.SheetId);
            ResizeJobs.Enqueue(target);
        

        public static void DoResizing()
        
            while (ResizeJobs.Count > 0)
            
                DoResize(ResizeJobs.Dequeue());
            
        

        static void DoResize(ExcelReference target)
        
            try
            
                // Get the current state for reset later

                XlCall.Excel(XlCall.xlcEcho, false);

                // Get the formula in the first cell of the target
                string formula = (string)XlCall.Excel(XlCall.xlfGetCell, 41, target);
                ExcelReference firstCell = new ExcelReference(target.RowFirst, target.RowFirst, target.ColumnFirst, target.ColumnFirst, target.SheetId);

                bool isFormulaArray = (bool)XlCall.Excel(XlCall.xlfGetCell, 49, target);
                if (isFormulaArray)
                
                    object oldSelectionOnActiveSheet = XlCall.Excel(XlCall.xlfSelection);
                    object oldActiveCell = XlCall.Excel(XlCall.xlfActiveCell);

                    // Remember old selection and select the first cell of the target
                    string firstCellSheet = (string)XlCall.Excel(XlCall.xlSheetNm, firstCell);
                    XlCall.Excel(XlCall.xlcWorkbookSelect, new object[] firstCellSheet);
                    object oldSelectionOnArraySheet = XlCall.Excel(XlCall.xlfSelection);
                    XlCall.Excel(XlCall.xlcFormulaGoto, firstCell);

                    // Extend the selection to the whole array and clear
                    XlCall.Excel(XlCall.xlcSelectSpecial, 6);
                    ExcelReference oldArray = (ExcelReference)XlCall.Excel(XlCall.xlfSelection);

                    oldArray.SetValue(ExcelEmpty.Value);
                    XlCall.Excel(XlCall.xlcSelect, oldSelectionOnArraySheet);
                    XlCall.Excel(XlCall.xlcFormulaGoto, oldSelectionOnActiveSheet);
                
                // Get the formula and convert to R1C1 mode
                bool isR1C1Mode = (bool)XlCall.Excel(XlCall.xlfGetWorkspace, 4);
                string formulaR1C1 = formula;
                if (!isR1C1Mode)
                
                    // Set the formula into the whole target
                    formulaR1C1 = (string)XlCall.Excel(XlCall.xlfFormulaConvert, formula, true, false, ExcelMissing.Value, firstCell);
                
                // Must be R1C1-style references
                object ignoredResult;
                XlCall.XlReturn retval = XlCall.TryExcel(XlCall.xlcFormulaArray, out ignoredResult, formulaR1C1, target);
                if (retval != XlCall.XlReturn.XlReturnSuccess)
                
                    // TODO: Consider what to do now!?
                    // Might have failed due to array in the way.
                    firstCell.SetValue("'" + formula);
                
            
            finally
            
                XlCall.Excel(XlCall.xlcEcho, true);
            
        

        // Most of this from the newsgroup: http://groups.google.com/group/exceldna/browse_thread/thread/a72c9b9f49523fc9/4577cd6840c7f195
        private static readonly TimeSpan BackoffTime = TimeSpan.FromSeconds(1); 
        static void AsyncRunMacro(string macroName)
        
            // Do this on a new thread....
            Thread newThread = new Thread( delegate ()
            
                while(true) 
                 
                    try 
                    
                        RunMacro(macroName);
                        break; 
                     
                    catch(COMException cex) 
                     
                        if(IsRetry(cex)) 
                         
                            Thread.Sleep(BackoffTime); 
                            continue; 
                         
                        // TODO: Handle unexpected error
                        return; 
                    
                    catch(Exception ex) 
                     
                        // TODO: Handle unexpected error
                        return;
                     
                
            );
            newThread.Start();
        

        static void RunMacro(string macroName)
        
            object xlApp = null;       
            try
            
                xlApp = ExcelDnaUtil.Application;
                xlApp.GetType().InvokeMember("Run", BindingFlags.InvokeMethod, null, xlApp, new object[] macroName);
            
            catch (TargetInvocationException tie)
            
                throw tie.InnerException;
            
            finally
            
                Marshal.ReleaseComObject(xlApp);
            
        

        const uint RPC_E_SERVERCALL_RETRYLATER = 0x8001010A; 
        const uint VBA_E_IGNORE = 0x800AC472; 
        static bool IsRetry(COMException e) 
         
            uint errorCode = (uint)e.ErrorCode; 
            switch(errorCode) 
             
                case RPC_E_SERVERCALL_RETRYLATER: 
                case VBA_E_IGNORE: 
                    return true; 
                default: 
                    return false; 
            
        
     
]]>

Aquí puedes ver las reseñas y valoraciones de los usuarios

Acuérdate de que puedes esclarecer si diste con la respuesta.

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *