अद्यतन: किसी कारण के लिए, पिछले कोड रहा था केवल काम किया जब पहुँच फ़ाइल स्वरूप 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
पर ऐसा करना अपेक्षाकृत मामूली है, क्योंकि इस वर्कबुक में कोड-बैक है और स्वचालित रूप से तुरंत चालू हो जाता है। लेकिन हम इसे एक्सेस में कैसे कर सकते हैं?
समाधान निम्नलिखित तरीके से ऊपर दो तकनीकों को जोड़ती है:
- प्रोग्राम के लक्ष्य पहुँच फ़ाइल में एक नया अस्थायी वर्ग मॉड्यूल, जिसके साथ एक्सेस डेटाबेस में लक्ष्य समारोह कॉल करने के लिए पैदा करते हैं। ध्यान रखें कि
Instancing
कक्षा की संपत्ति 2 - PublicNotCreatable
पर सेट की जानी चाहिए। इसका मतलब है कि कक्षा उस परियोजना के बाहर रचनात्मक नहीं है, लेकिन यह सार्वजनिक रूप से सुलभ है।
- लक्ष्य एक्सेस फ़ाइल में एक नया मानक मॉड्यूल संलग्न करें, और कक्षा को तुरंत चालू करने और इसे वापस करने के लिए एक सार्वजनिक फ़ंक्शन बनाएं।
- चरण (2) में वीबीए कोड को कॉल करके, अपने सी # कोड में ऑब्जेक्ट का संदर्भ प्राप्त करें। यह एक्सेस इंटरऑप के एप्लिकेशन का उपयोग करके किया जा सकता है। रुन()।
- कॉलबिन नाम का उपयोग करके ऑब्जेक्ट पर विधि (3) पर कॉल करें - जो मानक मॉड्यूल में विधि को कॉल करता है, और trappable है।
- जब आप डेटाबेस बंद करते हैं, तो आप पर
acQuitSaveNone
के साथ कॉल करते हैं, इसलिए आपके द्वारा अभी बनाए गए वीबीए कोड में से कोई भी सहेजा नहीं जाता है।
वीबीए त्रुटि विवरण प्राप्त करने के लिए, "ई.मेजेज" का उपयोग करें, जहां "ई" COMException ऑब्जेक्ट है।
करें कि आप अपने सी # परियोजना के लिए निम्न .NET संदर्भ जोड़ें बनाओ:
Microsoft.Office.Interop.Access
Microsoft.Vbe.Interop
Microsoft.VisualBasic
मैं Eval विधि के आसपास आज़माएं/कैच की कोशिश की है, और यह पूरी तरह से इसे अनदेखा करने लगता है। क्या आप अपने एक्सेल फ़ंक्शंस को कॉल करने के लिए इवल का उपयोग कर रहे हैं? – transistor1
नहीं, मैं नहीं हूं। मैंने सोचा होगा कि इंटरऑप इसे पकड़ लेगा और इसे अपने उपभोक्ता तक पहुंचाएगा। – squillman
तो, आप अपने एक्सेल कार्यपुस्तिका के भीतर से VBA फ़ंक्शंस को कॉल करने के लिए अपनी 'DoSomething()' विधि के अंदर क्या उपयोग करते हैं? मुझे आशा है कि मुझे कुछ याद आ रही है! – transistor1