简体   繁体   English

Microsoft Excel无法访问Windows Server 2012上的文件

[英]Microsoft Excel cannot access the file on Windows Server 2012

I am running an SSIS Package that contains a C# Script which formats an Excel File on Windows Server 2012 R2 . 我正在运行一个包含C#脚本的SSIS程序包,该程序在Windows Server 2012 R2上格式化Excel文件。

When I run the package it gives me this error Microsoft Office Excel cannot access the file '\\\\FolderPath\\FilePath' 当我运行程序包时,它给了我这个错误Microsoft Office Excel无法访问文件'\\\\ FolderPath \\ FilePath'

I have seen this question Microsoft Office Excel cannot access the file 'c:\\inetpub\\wwwroot\\Timesheet\\App_Data\\Template.xlsx' and have checked my permission's and they are correct. 我已经看到此问题, Microsoft Office Excel无法访问文件'c:\\ inetpub \\ wwwroot \\ Timesheet \\ App_Data \\ Template.xlsx' ,并检查了我的许可,并且它们是正确的。

I also tried to add Double Quotes around the final FilePath like this sFile = "\\"" + sFile + "\\""; 我还尝试在最终FilePath周围添加双引号 ,例如sFile = "\\"" + sFile + "\\""; but this outputs the error Microsoft Excel cannot access the file '"\\FolderPath\\FilePath" it is removing one \\ I really don't understand why. 但这会输出错误, Microsoft Excel无法访问文件“ \\ FolderPath \\ FilePath”,它正在删除一个\\我真的不明白为什么。

Below is the original code 下面是原始代码

using System;
using System.Data;
using Microsoft.SqlServer.Dts.Runtime;
using System.Windows.Forms;
using System.Collections.Generic;
using System.Data.SqlClient;
using Excel = Microsoft.Office.Interop.Excel;
using System.IO;
using System.Text;
using System.Diagnostics;
using System.Reflection;

public int Main()
    {

     StringBuilder sb = new StringBuilder();
     string LogFilePath = "\\\\LogFilePath";
     string  strExcelDataOutPut = "\\\\FolderPath"; 
     string sPath = "\\filePath";

    try {
        FormatFile(strExcelDataOutPut,sPath);

    } catch (Exception ex) {

using (System.IO.StreamWriter outfile = new System.IO.StreamWriter(LogFilePath))
       {
   sb.AppendLine("Error Occured ..Please see the error Message :" + ex.Message);
   outfile.Write(sb.ToString());

       }
   }

    }
 public void FormatFile(string strExcelDataOutPut, string sPath)
                {
                    Microsoft.Office.Interop.Excel.Application objExcelApp = new Excel.Application();
                    Microsoft.Office.Interop.Excel.Workbook objExcelWbk = default(Excel.Workbook);
                    Microsoft.Office.Interop.Excel.Worksheet objWrksheet = default(Excel.Worksheet);

                    object missing = Missing.Value;
                    Excel.Range crange1;

                    string sFile = string.Empty;
                    string sWorkSheet = string.Empty;

                    //--Month in English/French
                    string sMonthYear = string.Empty;

                    try
                    {
                        objExcelApp.DisplayAlerts = false;
                        objExcelApp.Visible = false;

                        sFile = strExcelDataOutPut + sPath;

                        //--Check if the file exists ---------------------------------------------------------
                        if (System.IO.File.Exists(sFile))
                        {
                            sWorkSheet = "Sheet1";
                        }

                        objExcelWbk = objExcelApp.Workbooks.Open(sFile.Trim(), missing, missing, missing, missing, missing, missing, missing, missing, missing, missing, missing, missing, missing, true);
                        objWrksheet = (Excel.Worksheet)objExcelWbk.Worksheets[sWorkSheet];
                        ((Microsoft.Office.Interop.Excel._Worksheet)objWrksheet).Activate();

                        //--Format
                        sMonthYear = "Report as at: " + DateTime.Today.ToString("MMMM") + " " + DateTime.Today.Day.ToString() + ", " + DateTime.Today.Year.ToString();
                        objWrksheet.PageSetup.LeftHeader = "&8&F";
                        //objWrksheet.PageSetup.CenterFooter = @"&12&""Arial,Bold" + sMonthYear;
                        objWrksheet.PageSetup.CenterFooter = " " + sMonthYear;

                        crange1 = (Excel.Range)objWrksheet.Cells[1, 1];
                        crange1.Select();

                        //objExcelWbk.SaveAs(sFile, missing, missing, missing, missing, missing, Microsoft.Office.Interop.Excel.XlSaveAsAccessMode.xlExclusive, missing, missing, missing, missing, missing);
                        //objExcelWbk.Close(true, missing, missing);
                        objExcelWbk.Save();
                        objExcelWbk.Close(true, sFile, missing);                
                        objExcelApp.Quit();
                    }
                    catch
                    {
                        throw;
                    }
                    finally
                    {
                        objWrksheet = null;
                        objExcelWbk = null;
                        objExcelApp = null;
                        System.GC.Collect();
                    }
                }

So I came across this Microsoft Blog . 所以我遇到了这个Microsoft Blog Although this is very weird that why you need to create the Desktop folders but it works. 尽管这很奇怪,为什么您需要创建Desktop文件夹,但是它可以工作。 In case the link gets removed see the Answer Below 如果链接被删除,请参见下面的答案

Resolution ************ · 解析度 ************ ·

A “Desktop” folder seems to be necessary in the “systemprofile” folder in the location C:\\Windows\\SysWOW64\\config\\ to open an Excel file · 在C:\\ Windows \\ SysWOW64 \\ config \\位置的“ systemprofile”文件夹中,似乎需要“桌面”文件夹来打开Excel文件·

Create the “Desktop” folder for Windows 2008 Server (x64) under the location C:\\Windows\\SysWOW64\\config\\systemprofile · 在位置C:\\ Windows \\ SysWOW64 \\ config \\ systemprofile下,为Windows 2008 Server(x64)创建“桌面”文件夹。

And for a 32 bit Windows 2008 Server create the “Desktop” folder under the location C:\\Windows\\System32\\config\\systemprofile · 对于32位Windows 2008 Server,在位置C:\\ Windows \\ System32 \\ config \\ systemprofile下创建“ Desktop”文件夹。

After creating the folder the SQL Server jobs should execute successfully 创建文件夹后,SQL Server作业应成功执行

The code feels like it's been too sanitized for the internet as it doesn't seem right with regard to your file work. 该代码似乎对Internet太过卫生了,因为在文件工作方面似乎不合适。

C# observations C#观察

Escaping a slash is downright painful due it also signalling a control character. 转义斜线是非常痛苦的,因为它也预示着控制字符。 C# allows you indicate a string is a verbatim string and then you don't have to deal with escaping everything. C#允许您指示字符串是逐字字符串 ,然后您不必处理所有转义。

In your Main, you have 在你的主要,你有

string strExcelDataOutPut = "\\\\FolderPath"; 
string sPath = "\\filePath";

When those strings are evaluated, you'd have values of \\\\FolderPath and \\filePath 评估这些字符串时,将具有\\\\FolderPath\\filePath

You combine them within the method call as 您将它们合并到方法调用中

sFile = strExcelDataOutPut + sPath;

which generates an UNC path as \\\\FolderPath\\filePath which is fine, but that's not a file name. 可以将UNC路径生成为\\\\FolderPath\\filePath ,这很好,但这不是文件名。 It seems that you're missing the actual file name, like 似乎您缺少实际的文件名,例如

sFile = strExcelDataOutPut + sPath + @"\Something.xlsx";

I have a trick for you on combining paths to make a valid path - there's a library and method for that. 对于组合路径以创建有效路径,我有一个窍门-为此提供了一个库和方法。 System.IO.Path.Combine It's doubly nice as you don't have to do as much escaping for the values there. System.IO.Path.Combine这很不错,因为您不必为其中的值做太多的转义操作。

sFile = Path.Combine(strExcelDataOutPut, sPath, "Something.xlsx");

Your check for file existence sets the worksheet name if the file is found but does nothing if the file isn't found. 如果找到文件,检查文件是否存在会设置工作表名称,如果找不到文件,则不执行任何操作。 Isn't that going to cause issues when you attempt to open the file that doesn't exist? 当您尝试打开不存在的文件时,这是否会引起问题? Or if Excel will create the file, then when you attempt to access a worksheet named (empty string) in the Worksheets collection, that's going to fail. 或者,如果Excel将创建文件,那么当您尝试访问Worksheets集合中名为(空字符串)的工作表时,该操作将失败。

Finally, beware of using the double dots in your method there. 最后,请注意在该方法中使用双点

SSIS observations SSIS观察

You don't specify where you're running this package and encountering issues but I can see a number of things that can go wrong. 您无需指定在何处运行此软件包并遇到问题,但是我可以看到很多可能出错的地方。

The first and foremost is licensing. 首先是许可。 As long as you, your DBAs and management understand that this implementation is going to require that you purchase a Microsoft Office license for your SQL Server instance(s). 只要您,您的DBA和管理人员都知道,此实现将需要您为SQL Server实例购买Microsoft Office许可证。 Office requires more frequent patching with potential reboots, than your typical server level products have. 与典型的服务器级产品相比,Office需要更频繁的修补程序以及潜在的重新启动。 This means if your SQL Server installations have a hard SLA, this implementation might encroach on that window. 这意味着,如果您的SQL Server安装具有硬SLA,则该实现可能会侵犯该窗口。

215 dollars out the door (as quoted by CDW for a home and business license) and our servers now get patched more frequently. 215美元(根据CDW的房屋和商业许可证报价),我们的服务器现在更频繁地获得补丁。 Awesome, now the server FolderPath dies, or the file share changes or anything happens and now you need to change that path - you're looking at opening the package up, editing the script value, saving it, checking it into version control (you do use version control, right?), and then submitting that into whatever change control process your employer has. 太棒了,现在服务器FolderPath死了,或者文件共享发生了更改,或者发生了任何事情,现在您需要更改该路径-您正在寻找打开包,编辑脚本值,保存,将其检入版本控制( 使用版本控制,不是吗?),然后提交到这一切的变更控制流程的雇主。 That's a lot of wasted time in my world. 那在我的世界里浪费了很多时间。

Instead, you can make 相反,您可以

 string LogFilePath = "\\\\LogFilePath";
 string  strExcelDataOutPut = "\\\\FolderPath"; 
 string sPath = "\\filePath";

into something like 变成像

 string LogFilePath = Dts.Variables["LogPath"].Value.ToString();
 string strExcelDataOutPut = Dts.Variables["ExcelPath"].Value.ToString();
 string sPath = Dts.Variables["FilePath"].Value.ToString();

What's that get you? 那能给你什么? Now you're running your script based on Variables within SSIS. 现在,您正在基于SSIS中的变量运行脚本。 You can make a change to those variables, either through Configuration or Parameterization (depending on deployment model) and then your package behaviour changes without code changes. 您可以通过“配置”或“参数化”(取决于部署模型)来更改这些变量,然后更改程序包的行为而无需更改代码。 You're updating an external value in some repository instead of code itself. 您正在更新某个存储库中的外部值,而不是代码本身。 In the environments I work in, that's generally a much lower risk change to implement versus a code change (even if all you did was change the value of a variable). 在我工作的环境中,与代码更改相比,实现的风险更改通常要低得多(即使您所做的只是更改变量的值)。

Unless you have a process already built out for consuming your custom logging there, stop and use the native tooling . 除非您已经建立了使用自定义日志记录的流程,否则请停止并使用本机工具 Raise an Information, Warning or Error message. 提出信息,警告或错误消息。 Otherwise, you're going to need to ensure SSIS can write to that location (more on this later). 否则,您将需要确保SSIS可以写入该位置(稍后将对此进行详细介绍)。

Dts.Events.FireError(0, "Excel Writer", "Error Occurred...Please see the error Message :" + ex.Message, String.Empty, 0); 

Now when your package is running, that information will be dumped to the Error handler. 现在,当您的程序包运行时,该信息将被转储到错误处理程序中。 If you're using the Project Deployment Model, that information will automatically be logged to the SSISDB.catalog.operation_messages table and shows up in your friendly reports. 如果使用的是Project Deployment Model,该信息将自动记录到SSISDB.catalog.operation_messages表中,并显示在友好的报告中。 Otherwise, go configure logging and SSIS will write those events to a table, flat file, event viewer, profiler and/or an XML file. 否则,请进行配置日志记录 ,SSIS会将这些事件写入表,平面文件,事件查看器,事件探查器和/或XML文件。 The important thing is you don't reinvent the wheel unless you have a really good reason. 重要的是,除非有充分的理由,否则不要重新发明轮子。

When you're running this SSIS package, you'll need to ensure you are using the correct version of dtexec relative to your 32 or 64 bit version of Office. 运行此SSIS包时,需要确保相对于32位或64位版本的Office使用的是正确版本的dtexec。 This bites many people in the backside. 这在后面咬了很多人。 Office generally defaults to a 32 bit installation, which is a pity. Office通常默认使用32位安装,这很遗憾。

When this package is operationalized, ie it is running on SQL Server, probably as part of a SQL Agent job, the Account that runs SQL Agent will likely need a higher level of permissions that is generally granted to the account. 当此程序包投入运行后, 在SQL Server上运行(可能是作为SQL Agent作业的一部分),运行SQL Agent的帐户可能需要通常授予该帐户的更高级别的权限。 It'll need network access to wherever that file is shared to. 无论文件共享到哪里,都需要网络访问权限。 It will need InteractWithDesktop to be able to instantiate an instance of Excel and then do the needful. 它将需要InteractWithDesktop能够实例化Excel实例,然后进行必要的操作。 The SQL Agent job step will need to ensure that it has correctly flagged the SSIS package to be run as 32 bit instead of 64 (default). SQL Agent作业步骤将需要确保已正确标记SSIS程序包以32位而不是64 (默认)运行。 If you didn't implement logging and you're not using the Project Deployment Model, then the package generates an error, the error message will be captured within the SQL Agent job history. 如果您未实现日志记录,并且未使用项目部署模型,则程序包将生成错误,该错误消息将在SQL Agent作业历史记录中捕获。 That'll be useful at some point. 在某些时候这将很有用。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM