2012-03-12 17 views
7

मेरे पास एक सी # प्रोग्राम है जो कई माइक्रोसॉफ्ट एक्सेस फाइलें खोलता है, और प्रत्येक के भीतर से कार्य निष्पादित करता है।मैं अपने सी # कोड से माइक्रोसॉफ्ट एक्सेस वीबीए डीबग त्रुटि कैप्चर कैसे कर सकता हूं?

Microsoft.Office.Interop.Access.Application app = 
    new Microsoft.Office.Interop.Access.Application(); 

app.Visible = true; 
app.OpenCurrentDatabase(accessFileFullPath, false, ""); 

//Call the function 
app.Eval(function); 

हालांकि, जब एक डीबग त्रुटि VBA कोड में होता है, मैं अपने सी # कार्यक्रम में यह जाल चाहते हैं:

अनिवार्य रूप से, कोड कुछ इस तरह लग रहा है।

कृपया उत्तर न दें: "अपने वीबीए प्रोग्राम में त्रुटि को फंसें"। जिन कारणों से मैं नहीं जाऊंगा, उनके लिए यह संभव नहीं है।

एक विधि जिसे मैंने अतीत में उपयोग किया है, किसी थ्रेड को किसी भी विजुअल बेसिक डीबग विंडो (FindWindowEx Win32 फ़ंक्शन एक nonzero मान देता है) के लिए एक हैंडल के लिए मॉनिटर की निगरानी करता है। मुझे इस विधि को पसंद नहीं है, और इसका उपयोग जारी रखना नहीं चाहते हैं।

मुझे this thread मिला, जो माइक्रोसॉफ्ट एक्सेल पर लागू होता है। संक्षेप में, यह Microsoft.VisualBasic.CallByName() फ़ंक्शन का उपयोग करता है, जो स्पष्ट रूप से उपयोगकर्ता इंटरैक्शन के बिना प्रयास/पकड़ ब्लॉक में फंस सकता है। हालांकि, मैं इसे माइक्रोसॉफ्ट एक्सेस के साथ काम करने में सक्षम नहीं हूं - मुख्य रूप से क्योंकि मैं इस आदेश का उपयोग करके फ़ंक्शन/उप को कॉल करने का तरीका नहीं समझ सकता।

किसी भी सुझाव की ईमानदारी से सराहना की जाएगी!

संपादित करें: मैं नीचे जवाब में से एक में उल्लेख किया है, मैं एक आज़माएं/कैच ब्लॉक में Eval() और मेरी सी # कार्यक्रम इसे अनदेखा करने लगता है लपेटकर की कोशिश की है, जब तक कोई उपयोगकर्ता हिट पर "का अर्थ अंतिम" बटन "माइक्रोसॉफ्ट विजुअल बेसिक" त्रुटि संवाद। मैं कोई उपयोगकर्ता इंटरैक्शन नहीं चाहता हूं, बल्कि मेरे सी # प्रोग्राम में हैंडलिंग के लिए वीबीए त्रुटि को फंसाना चाहता हूं।

उत्तर

4

अद्यतन: किसी कारण के लिए, पिछले कोड रहा था केवल काम किया जब पहुँच फ़ाइल स्वरूप 2000 मैंने पुष्टि की है कि इस नए कोड भी पहुँच 2002 और 2010 फाइलों के साथ काम करता है था।

कोड:

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Windows.Forms; 
using System.Runtime.InteropServices; 
using VBA = Microsoft.Vbe.Interop; 

namespace CaptureVBAErrorsTest 
{ 
    class CaptureVBAErrors 
    { 
     public void runApp(string databaseName, string function) 
     { 
      VBA.VBComponent f = null; 
      VBA.VBComponent f2 = null; 
      Microsoft.Office.Interop.Access.Application app = null; 
      object Missing = System.Reflection.Missing.Value; 
      Object tempObject = null; 

      try 
      { 
       app = new Microsoft.Office.Interop.Access.Application(); 
       app.Visible = true; 
       app.OpenCurrentDatabase(databaseName, false, ""); 

       //Step 1: Programatically create a new temporary class module in the target Access file, with which to call the target function in the Access database 

       //Create a Guid to append to the object name, so that in case the temporary class and module somehow get "stuck", 
       //the temp objects won't interfere with other objects each other (if there are multiples). 
       string tempGuid = Guid.NewGuid().ToString("N"); 

       f = app.VBE.ActiveVBProject.VBComponents.Add(VBA.vbext_ComponentType.vbext_ct_ClassModule); 

       //We must set the Instancing to 2-PublicNotCreatable 
       f.Properties.Item("Instancing").Value = 2; 
       f.Name = "TEMP_CLASS_" + tempGuid; 
       f.CodeModule.AddFromString(
        "Public Sub TempClassCall()\r\n" + 
        " Call " + function + "\r\n" + 
        "End Sub\r\n"); 

       //Step 2: Append a new standard module to the target Access file, and create a public function to instantiate the class and return it. 
       f2 = app.VBE.ActiveVBProject.VBComponents.Add(VBA.vbext_ComponentType.vbext_ct_StdModule); 
       f2.Name = "TEMP_MODULE_" + tempGuid 
       f2.CodeModule.AddFromString(string.Format(
        "Public Function instantiateTempClass_{0}() As Object\r\n" + 
        " Set instantiateTempClass_{0} = New TEMP_CLASS_{0}\r\n" + 
        "End Function" 
        ,tempGuid)); 

       //Step 3: Get a reference to a new TEMP_CLASS_* object 
       tempObject = app.Run("instantiateTempClass_" + tempGuid, ref Missing, ref Missing, ref Missing, ref Missing, ref Missing, ref Missing, ref Missing, ref Missing, ref Missing, ref Missing, ref Missing, ref Missing, ref Missing, ref Missing, ref Missing, ref Missing, ref Missing, ref Missing, ref Missing, ref Missing, ref Missing, ref Missing, ref Missing, ref Missing, ref Missing, ref Missing, ref Missing, ref Missing, ref Missing, ref Missing); 

       //Step 4: Call the method on the TEMP_CLASS_* object. 
       Microsoft.VisualBasic.Interaction.CallByName(tempObject, "TempClassCall", Microsoft.VisualBasic.CallType.Method); 
      } 
      catch (COMException e) 
      { 
       MessageBox.Show("A VBA Exception occurred in file:" + e.Message); 
      } 
      catch (Exception e) 
      { 
       MessageBox.Show("A general exception has occurred: " + e.StackTrace.ToString()); 
      } 
      finally 
      { 
       //Clean up 
       if (f != null) 
       { 
        app.VBE.ActiveVBProject.VBComponents.Remove(f); 
        Marshal.FinalReleaseComObject(f); 
       } 

       if (f2 != null) 
       { 
        app.VBE.ActiveVBProject.VBComponents.Remove(f2); 
        Marshal.FinalReleaseComObject(f2); 
       } 

       if (tempObject != null) Marshal.FinalReleaseComObject(tempObject); 

       if (app != null) 
       { 
        //Step 5: When you close the database, you call Application.Quit() with acQuitSaveNone, so none of the VBA code you just created gets saved. 
        app.Quit(Microsoft.Office.Interop.Access.AcQuitOption.acQuitSaveNone); 
        Marshal.FinalReleaseComObject(app); 
       } 

       GC.Collect(); 
       GC.WaitForPendingFinalizers(); 
      } 
     } 
    } 
} 

विवरण:

the thread I had linked toMike Rosenblum द्वारा के अनुसार, CallByName() सी # में कार्यालय कोड निष्पादित कर सकते हैं, और कर सकते हैं जाल VBA अपवाद (Application.Run() और Application.Eval() ऐसा लगता है कि उपयोगकर्ता डीबग विंडो के साथ इंटरैक्ट करने के बाद पकड़ा जाता है)। समस्या यह है कि CallByName() को एक विधि को कॉल करने के लिए एक [तत्काल] ऑब्जेक्ट की आवश्यकता होती है। डिफ़ॉल्ट रूप से, एक्सेल में ThisWorkbook ऑब्जेक्ट होता है, जिसे कार्यपुस्तिका खोलने पर तत्काल किया जाता है। एक्सेस में ऐसी ही ऑब्जेक्ट नहीं है जो सुलभ है, जहां तक ​​मुझे पता है।

A subsequent post on the same thread मानक मॉड्यूल में विधियों को कॉल करने की अनुमति देने के लिए Excel कार्यपुस्तिका में गतिशील रूप से कोड जोड़ने का सुझाव देता है। ThisWorkbook पर ऐसा करना अपेक्षाकृत मामूली है, क्योंकि इस वर्कबुक में कोड-बैक है और स्वचालित रूप से तुरंत चालू हो जाता है। लेकिन हम इसे एक्सेस में कैसे कर सकते हैं?

समाधान निम्नलिखित तरीके से ऊपर दो तकनीकों को जोड़ती है:

  1. प्रोग्राम के लक्ष्य पहुँच फ़ाइल में एक नया अस्थायी वर्ग मॉड्यूल, जिसके साथ एक्सेस डेटाबेस में लक्ष्य समारोह कॉल करने के लिए पैदा करते हैं। ध्यान रखें कि Instancing कक्षा की संपत्ति 2 - PublicNotCreatable पर सेट की जानी चाहिए। इसका मतलब है कि कक्षा उस परियोजना के बाहर रचनात्मक नहीं है, लेकिन यह सार्वजनिक रूप से सुलभ है।
  2. लक्ष्य एक्सेस फ़ाइल में एक नया मानक मॉड्यूल संलग्न करें, और कक्षा को तुरंत चालू करने और इसे वापस करने के लिए एक सार्वजनिक फ़ंक्शन बनाएं।
  3. चरण (2) में वीबीए कोड को कॉल करके, अपने सी # कोड में ऑब्जेक्ट का संदर्भ प्राप्त करें। यह एक्सेस इंटरऑप के एप्लिकेशन का उपयोग करके किया जा सकता है। रुन()।
  4. कॉलबिन नाम का उपयोग करके ऑब्जेक्ट पर विधि (3) पर कॉल करें - जो मानक मॉड्यूल में विधि को कॉल करता है, और trappable है।
  5. जब आप डेटाबेस बंद करते हैं, तो आप पर acQuitSaveNone के साथ कॉल करते हैं, इसलिए आपके द्वारा अभी बनाए गए वीबीए कोड में से कोई भी सहेजा नहीं जाता है।

वीबीए त्रुटि विवरण प्राप्त करने के लिए, "ई.मेजेज" का उपयोग करें, जहां "ई" COMException ऑब्जेक्ट है।

करें कि आप अपने सी # परियोजना के लिए निम्न .NET संदर्भ जोड़ें बनाओ:

Microsoft.Office.Interop.Access 
Microsoft.Vbe.Interop 
Microsoft.VisualBasic 
3

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

try { 
    DoSomething(); 
} catch (COMException ex) { 
    Console.WriteLine ex.ErrorCode.ToString(); 
    Console.WriteLine ex.Message; 
} 
+0

मैं Eval विधि के आसपास आज़माएं/कैच की कोशिश की है, और यह पूरी तरह से इसे अनदेखा करने लगता है। क्या आप अपने एक्सेल फ़ंक्शंस को कॉल करने के लिए इवल का उपयोग कर रहे हैं? – transistor1

+1

नहीं, मैं नहीं हूं। मैंने सोचा होगा कि इंटरऑप इसे पकड़ लेगा और इसे अपने उपभोक्ता तक पहुंचाएगा। – squillman

+0

तो, आप अपने एक्सेल कार्यपुस्तिका के भीतर से VBA फ़ंक्शंस को कॉल करने के लिए अपनी 'DoSomething()' विधि के अंदर क्या उपयोग करते हैं? मुझे आशा है कि मुझे कुछ याद आ रही है! – transistor1

संबंधित मुद्दे

 संबंधित मुद्दे