Excel Interop C# Can't add VBProject Error 800

2019-09-09 10:28发布

问题:

I) Global Explanation

Hello,

I'm developing an app in C# using Microsoft.Office.Interop.Excel. In my case I implement some olds macros stored in a file to an Excel Workbook, opened by C#, to upgrade them without re-coding macros in my app.

Globally there is my process in my app :

  • The user select an updated macro,
  • I check if the macro require something :
    • Here I set the register Key CURRENT_USER\SOFTWARE\Microsoft\Office\[office_ID].0\Excel\Security\AccessVBOM to 1 for allows macro/VBProject modifications
  • I instantiate an Excel app with a new workbook
  • I set optimization Excel execution parameters (disable refresh... that kind of stuff)
  • I add my macro, stored in a file, to the workbook
  • I execute the macro through Excel
  • I remove the macro from the Excel
  • I unset optimizations Excel executions parameters
  • I reset registers keys (.../AccessVBOM = 0)

II) My problem and my research result

In the "I add my macro, stored in a file, to the workbook" part, when I used the following instruction to add a VBProject to the workbook (required to add the macro) I get an error 800A03EC.

/// wb is a workbook object
VBComponent module = wb.VBProject.VBComponents.Add(vbext_ComponentType.vbext_ct_StdModule);

This error is send with a message (grossly traduce) : "The access to VB Project are not trust."

As I see on the Internet, this error occurs when an option in Excel isn't correctly set. This option can by handle with the CURRENT_USER\SOFTWARE\Microsoft\Office\[office_ID].0\Excel\Security\AccessVBOM register key. And I'm building my app with a register key management for avoid this problem.

And as you will see on the debug display part, we can see the option is correctly set on the execution.

My question : there is another case when this error occurs?

III) Debug analyse

When I catch the ComException, I use the following to display registers keys values.

/// RequirementManager is my own class, use to set and unset endpoints computers parameters, before and after each user action
RegistryKey reg;
for (int i = RequirementManager.EXCEL_ID_HANDLED_MIN; i < RequirementManager.EXCEL_ID_HANDLED_MAX; i++)
{
    reg = Registry.CurrentUser.CreateSubKey(@"SOFTWARE\Microsoft\Office\" + i + @".0\Excel\Security\", RegistryKeyPermissionCheck.ReadSubTree);
    Debug.WriteLine(String.Format(@"Value of CURRENT_USER\SOFTWARE\Microsoft\Office\{0}.0\Excel\Security\AccessVBOM = {1}", i, reg.GetValue("AccessVBOM")), "Debug");
    reg.Close();
}
reg = null;

And here is the console output of this lines :

Debug: Value of CURRENT_USER\SOFTWARE\Microsoft\Office\11.0\Excel\Security\AccessVBOM = 1
Debug: Value of CURRENT_USER\SOFTWARE\Microsoft\Office\12.0\Excel\Security\AccessVBOM = 1
Debug: Value of CURRENT_USER\SOFTWARE\Microsoft\Office\13.0\Excel\Security\AccessVBOM = 1
Debug: Value of CURRENT_USER\SOFTWARE\Microsoft\Office\14.0\Excel\Security\AccessVBOM = 1
Debug: Value of CURRENT_USER\SOFTWARE\Microsoft\Office\15.0\Excel\Security\AccessVBOM = 1
Debug: Value of CURRENT_USER\SOFTWARE\Microsoft\Office\16.0\Excel\Security\AccessVBOM = 1

As you can see, the option is correctly set when the error occurs. And all instance of Excel are closed before the modification of register keys, for avoid problem. I use similar algorithm to modify this range of register keys, like a lazy boy.


Any idea are welcome

Regards, Dykoine


IV) Answer Reaction

@prizm1 This parameter if macro are allowed or not (display orange warning message), and on my computer (while test) I set it to allow all macro.

After test, I can see this option doesn't protect the user, because we can open an Excel file with C# and execute a macro in it without be block by this option even if you use "disabled all without notification".

回答1:

Here the code which haven't made any problems with putting VBA into an Excel file. Maybe you need to save it to disk first before entering the Code, since the Project is determined by the filename. Just an addition, and no real solution:

    public bool injectVBA(String scriptText, String target)
    {
        VBProject found = null;
        Access.Application currApplication = this.currentInstance.Application;
        if (target.Equals("") || scriptText.Equals(""))
            return false;
        foreach (VBProject vb in currApplication.VBE.VBProjects)
        {
            if (currApplication.CurrentDb().Name.Equals(vb.FileName))
            {
                found = vb;
                break;
            }
        }

        if (found != null)
        {
            foreach (VBComponent foundComponent in found.VBComponents) 
            {
                if (foundComponent.Name.Equals(target))
                {
                    return true;
                }
            }
            VBComponent module = found.VBComponents.Add(vbext_ComponentType.vbext_ct_StdModule);
            module.Name = target;
            module.CodeModule.AddFromString(scriptText);
            module.Activate();
            //currApplication.DoCmd.OpenModule(target, Type.Missing);
            currApplication.DoCmd.Save(Access.AcObjectType.acModule, target);
            return true;
        }
        else
        {
            return false;
        }
    }