2012-03-12 3 views
7

मैं ब्लूमबर्ग बीडीएच व्यवहार की प्रतिलिपि बनाना चाहता हूं।एक्सेल एक्सेलडीएनए सी #/ब्लूमबर्ग बीडीएच() व्यवहार (वेब ​​अनुरोध के बाद ऐरे लिखना) की प्रतिलिपि बनाने का प्रयास करें

बीडीएच एक वेब अनुरोध करता है और एक सरणी लिखता है (लेकिन एक सरणी शैली वापस नहीं करता है)। इस वेब अनुरोध के दौरान, फ़ंक्शन "# एन/ए अनुरोध" देता है। जब वेब अनुरोध समाप्त हो गया, तो बीडीएच() फ़ंक्शन वर्कशीट में सरणी परिणाम लिखता है।

उदाहरण के लिए, एक्सेलडीएनए में, मैं वर्कशीट में थ्रेड के साथ लिखने में सफल होता हूं।

परिणाम आप एक डीएनए फ़ाइल में नीचे दिए गए कोड,

= WriteArray का परिणाम का उपयोग करता है, तो (2; 2)

पंक्ति 1>#N/A Requesting Data (0,1)

लाइन हो जाएगा 2>(1,0) (1,1)

अंतिम अंकको प्रतिस्थापित करना है मूल्य के साथऔर सूत्र की प्रतिलिपि बनाएँ। आप टिप्पणी हटाएं //xlActiveCellType.InvokeMember("FormulaR1C1Local ", आप परिणाम के पास हैं, लेकिन आप सही व्यवहार

फ़ाइल नहीं है जब .dna

<DnaLibrary Language="CS" RuntimeVersion="v4.0"> 
<![CDATA[ 

using System; 
using System.Collections.Generic; 
using System.Reflection; 
using System.Runtime.InteropServices; 
using System.Threading; 
using ExcelDna.Integration; 


    public static class WriteForXL 
    { 

     public static object[,] MakeArray(int rows, int columns) 
     { 
      if (rows == 0 && columns == 0) 
      { 
       rows = 1; 
       columns = 1; 
      } 


      object[,] result = new string[rows, columns]; 
      for (int i = 0; i < rows; i++) 
      { 
       for (int j = 0; j < columns; j++) 
       { 
        result[i, j] = string.Format("({0},{1})", i, j); 
       } 
      } 

      return result; 
     } 

     public static object WriteArray(int rows, int columns) 
     { 
      if (ExcelDnaUtil.IsInFunctionWizard()) 
       return "Waiting for click on wizard ok button to calculate."; 

      object[,] result = MakeArray(rows, columns); 

      var xlApp = ExcelDnaUtil.Application; 
      Type xlAppType = xlApp.GetType(); 
      object caller = xlAppType.InvokeMember("ActiveCell", BindingFlags.GetProperty, null, xlApp, null); 
      object formula = xlAppType.InvokeMember("FormulaR1C1Local", BindingFlags.GetProperty, null, caller, null); 

      ObjectForThread q = new ObjectForThread() { xlRef = caller, value = result, FormulaR1C1Local = formula }; 

      Thread t = new Thread(WriteFromThread); 
      t.Start(q);    

      return "#N/A Requesting Data"; 
     } 

     private static void WriteFromThread(Object o) 
     { 
      ObjectForThread q = (ObjectForThread) o; 

      Type xlActiveCellType = q.xlRef.GetType(); 

      try 
      { 
       for (int i = 0; i < q.value.GetLength(0); i++) 
       { 
        for (int j = 0; j < q.value.GetLength(1); j++) 
        { 
         if (i == 0 && j == 0) 
          continue; 

         Object cellBelow = xlActiveCellType.InvokeMember("Offset", BindingFlags.GetProperty, null, q.xlRef, new object[] { i, j }); 
         xlActiveCellType.InvokeMember("Value", BindingFlags.SetProperty, null, cellBelow, new[] { Type.Missing, q.value[i, j] });    
        } 
       }        
      } 
      catch(Exception e) 
      {     
      } 
      finally 
      { 
       //xlActiveCellType.InvokeMember("Value", BindingFlags.SetProperty, null, q.xlRef, new[] { Type.Missing, q.value[0, 0] }); 
       //xlActiveCellType.InvokeMember("FormulaR1C1Local", BindingFlags.SetProperty, null, q.xlRef, new [] { q.FormulaR1C1Local });    
      } 
     } 

public class ObjectForThread 
     { 
      public object xlRef { get; set; } 
      public object[,] value { get; set; } 
      public object FormulaR1C1Local { get; set; } 
     } 

    } 

    ]]> 

</DnaLibrary> 

@To Govert

बीडीएच वित्त उद्योग में एक मानक बन गया है। लोगों को पता नहीं है कि कैसे एक सरणी (यहां तक ​​कि Ctrl + Shift + Enter) में हेरफेर करना है।

बीडीएच वह कार्य है जिसने ब्लूमबर्ग को इतना लोकप्रिय बनाया (रायटर के नुकसान के लिए)।

हालांकि मैं आपकी विधि या आरटीडी का उपयोग करने के बारे में सोचूंगा।

एक्सेल डीएनए में अपने सभी काम के लिए धन्यवाद

+0

पूरा होने पर बीडीएच() सेल (0,0) में क्या होता है, एक मूल्य या सूत्र? – Govert

+0

यदि आप सीएसी 40 इंडेक्स '= बीडीएच (" सीएसी इंडेक्स ";" पीएक्स_LAST ";" 20/05/2010 ";" 20/05/2010 "का इतिहास चाहते हैं;" 20/05/2012 "), आपके पास सेल (0,0) एक मान और सूत्र में = बीडीएच ("सीएसी इंडेक्स"; "पीएक्स_एलएसटी"; "20/05/2010"; "20/05/2012"; "कोल्स = 2; पंक्तियां = 478") '20/तो अपने समारोह में सिर्फ वापस लिखें - 05/2010 \t 3432,52 21/05/2010 \t 3430,74 .... ' बीडीएच छोड़कर जब अपने पत्र में डेटा existeing – Farandole

+0

ठीक डेटा ओवरराइड Excel के सभी मानक का सम्मान सेल (0,0) के लिए एक संशोधित सूत्र, और फ़ंक्शन को उस मान को वापस करने के लिए संशोधित पैरामीटर का पता लगाएं, जिसे आप वहां प्रदर्शित करना चाहते हैं। मुझे लगता है कि यह बीडीएच करता है जब यह अंतिम तर्क के रूप में "cols = 2; पंक्तियों = 478" को देखता है। – Govert

उत्तर

1

मेरे मुद्दा था:

  • गतिशील सरणी लिख

  • डेटा एक वेब सेवा

के माध्यम से अतुल्यकालिक प्राप्त किए गए हैं Govert के साथ चर्चा के बाद, मैं एक के रूप में एक परिणाम लेने के लिए चुना है सरणी और ब्लूमबर्ग फ़ंक्शंस की प्रतिलिपि न लें (एक सरणी लिखें लेकिन एक मान वापस करें)।

अंत में, मेरी समस्या को हल करने के लिए, मैंने http://excel-dna.net/2011/01/30/resizing-excel-udf-result-arrays/ का उपयोग किया और resize() फ़ंक्शन को दोबारा बदल दिया।

यह कोड आरटीडी नहीं है।

एक .dna फ़ाइल

<DnaLibrary RuntimeVersion="v4.0" Language="C#"> 
<![CDATA[ 
    using System; 
    using System.Collections.Generic; 
    using System.Reflection; 
    using System.Runtime.InteropServices; 
    using System.Threading; 
    using System.ComponentModel; 
    using ExcelDna.Integration; 

    public static class ResizeTest 
    { 
     public static object[,] MakeArray(int rows, int columns) 
     { 
      object[,] result = new string[rows, columns]; 
      for (int i = 0; i < rows; i++) 
      { 
       for (int j = 0; j < columns; j++) 
       { 
        result[i,j] = string.Format("({0},{1})", i, j); 
       } 
      } 

      return result; 
     } 

     public static object MakeArrayAndResize() 
     {   
      // Call Resize via Excel - so if the Resize add-in is not part of this code, it should still work. 
      return XlCall.Excel(XlCall.xlUDF, "Resize", null); 
     } 
    } 

    public class Resizer 
    { 
     static Queue<ExcelReference> ResizeJobs = new Queue<ExcelReference>(); 
     static Dictionary<string, object> JobIsDone = new Dictionary<string, object>(); 

     // 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; 
      } 
     } 

     /// <summary> 
     /// Simulate WebServiceRequest 
     /// </summary> 
     /// <param name="caller"></param> 
     /// <param name="rows"></param> 
     /// <param name="columns"></param> 
     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; 
      } 
     } 
    } 
]]> 
</DnaLibrary> 
+0

यह कोड काम नहीं कर रहा है। MakeArray बस सेल में '(0,0)' प्रिंट करता है। MakeArrayAndResize कॉल को आकार बदलें, लेकिन कक्ष – mike01010

0

मैं तुम्हें एक RTD सर्वर के रूप में अनुरोध कार्यान्वित करने की जरूरत है लगता है। सामान्य उपयोगकर्ता परिभाषित फ़ंक्शन असीमित रूप से अपडेट नहीं होंगे।
फिर आप उपयोगकर्ता परिभाषित फ़ंक्शन के माध्यम से आरटीडी सर्वर की कॉल छुपा सकते हैं, जिसे एक्सेल-डीएनए के माध्यम से किया जा सकता है।

6

मुझे लगता है कि आपने Excel-DNAArrayResizer नमूना का प्रयास किया है, जो सावधानीपूर्वक आपके द्वारा चलाए जा रहे कई मुद्दों से बचाता है। मैं समझना चाहता हूं कि आप सरणी-सूत्र-लेखन दृष्टिकोण के नुकसान के रूप में क्या देखते हैं।

अब, अपने कार्य के बारे में:

सबसे पहले, आप सुरक्षित रूप से एक और धागा करने के लिए 'फोन करने वाले' रेंज COM वस्तु पारित नहीं कर सकते हैं - बल्कि एक स्ट्रिंग पते के साथ गुजरती हैं, और अन्य धागे से COM वस्तु मिल (कार्यकर्ता धागे पर ExcelDnaUtil.Aplication पर एक कॉल का उपयोग कर)। अधिकांश समय आप भाग्यशाली हो जाएंगे, हालांकि। ऐसा करने का बेहतर तरीका कार्यकर्ता थ्रेड से एक्सेल को मुख्य थ्रेड पर मैक्रो चलाने के लिए प्राप्त करना है - एप्लिकेशन को कॉल करके। रुन। एक्सेल-डीएनए ArrayResizer नमूना दिखाता है कि यह कैसे किया जा सकता है।

दूसरा, आप लगभग निश्चित रूप से ActiveCell नहीं चाहते हैं, बल्कि आवेदन। कॉलर। ActiveCell के पास सेल के साथ कुछ भी नहीं हो सकता है जहां सूत्र चल रहा है।

अगला - जब भी आप फॉर्मूला को फिर से सेट करते हैं तो एक्सेल आपके फ़ंक्शन को फिर से गणना करेगा - इसलिए जब आप अपने अंतिम खंड में फॉर्मूला सेट को सक्षम करते हैं तो आपको अंतहीन पाश में डाल दिया जाता है। आप सेल के लिए मान और सूत्र दोनों सेट नहीं कर सकते हैं - यदि किसी कक्ष में फ़ॉर्मूला है तो Excel मूल्य की गणना करने के लिए सूत्र का उपयोग करेगा। यदि आप मान सेट करते हैं, तो फॉर्मूला हटा दिया जाता है। यह स्पष्ट नहीं है कि आप वास्तव में [0,0] सेल में क्या छोड़ना चाहते हैं - आईआईआरसी ब्लूमबर्ग फॉर्मूला को इस तरह से संशोधित करता है जिससे यह याद रखता है कि कितनी बड़ी रेंज लिखी गई थी। आप अपने फ़ंक्शन में कुछ पैरामीटर जोड़ने का प्रयास कर सकते हैं जो आपके फ़ंक्शन को बताते हैं कि पुन: गणना करना है या इसके परिणामस्वरूप वास्तविक मान वापस करना है या नहीं।

अंत में, आप पुनर्विचार करना चाहेंगे कि क्या ब्लूमबर्ग बीडीएच फ़ंक्शन आप जो करना चाहते हैं उसके लिए एक अच्छा उदाहरण है। यह आपकी शीट की निर्भरता गणना को तोड़ता है, जिसमें प्रदर्शन के लिए और स्प्रेडशीट मॉडल की स्थिरता बनाए रखने के लिए दोनों प्रभाव पड़ते हैं।

+1

"आप सेल के लिए मूल्य और फॉर्मूला दोनों सेट नहीं कर सकते हैं" अनुमान लगाएं कि चाल पहले परिणाम # एन/ए अनुरोध डेटा (0,1) "फॉर्मूला के परिणामस्वरूप होगी, और जब आप एक्सेल प्राप्त करेंगे फॉर्मूला 'सेल का आकलन करें। गणना करें) और फिर "पूर्ण" या जो कुछ भी लौटाएं। एक स्थैतिक ध्वज रखें जो इंगित करता है कि कौन सा दिखाना चाहिए ... एक हैक का बिट लेकिन मुझे लगता है कि यह काम कर सकता है। (और ExcelDNA के लिए धन्यवाद!) – gjvdkamp

-2

तो अंत में आप सरणी सूत्र, सही प्रयोग कोड काम करता है belows? जैसा कि आपने कहा था, उपयोगकर्ता सरणी सूत्र से परिचित नहीं हैं, वे ctrl + shift + enter नहीं जानते हैं। मुझे लगता है कि सरणी सूत्र उनके लिए एक बड़ी समस्या है।

मेरे लिए, मेरे पास एक ही समस्या है। मैं इसके लिए प्रोटोटाइप बनाने की कोशिश कर रहा हूं। देखें https://github.com/kchen0723/ExcelAsync.git

+0

पर कोई मान लिखा नहीं गया है। यह प्रोजेक्ट अभी भी प्रगति पर है। । –