5

我正在使用 C# framework 4.5、netoffice 1.6 和 sharpdevelop 4.4.1 从 Outlook 中操作位于网络共享上的 excel 工作簿。

在某些时候,我需要将工作簿对象(ewb)的文件访问更改为读写,如下所示:

ewb.ChangeFileAccess(Excel.Enums.XlFileAccess.xlReadWrite, System.Reflection.Missing.Value, true);

在更改文件访问权限之前,我会检查文件是否在服务器上被锁定。如果文件被锁定,我将通知用户稍后重试该操作。

现在,我想在通知中包含锁定 excel 文件的用户名。我已经搜索了 msdn、netoffice 论坛等...并没有找到解决方案。我知道,如果您以读写方式打开 excel 文件,它会将用户名存储在 xlsx 文件中。如何通过 c# 访问该特定信息?

编辑:我最终这样做了:

public string GetExcelFileOwner(string path, NetOffice.ExcelApi.Enums.XlFileFormat ffmt) {
        string tempmark = "~$";
        if(ffmt==NetOffice.ExcelApi.Enums.XlFileFormat.xlExcel8) {
            tempmark = "";
        }
        string uspath = Path.Combine(Path.GetDirectoryName(path), tempmark + Path.GetFileName(path));
        if (!File.Exists(uspath)) return "";
        var sharing = FileShare.ReadWrite | FileShare.Delete;
        using (var fs = new FileStream(uspath, FileMode.Open, FileAccess.Read, sharing))
        using (var br = new BinaryReader(fs, Encoding.Default)) {
            if(ffmt==NetOffice.ExcelApi.Enums.XlFileFormat.xlExcel8) {
                byte[] ByteBuffer = new byte[500];
                br.BaseStream.Seek(150, SeekOrigin.Begin);
                br.Read(ByteBuffer, 0, 500);
                return matchRegex(System.Text.Encoding.UTF8.GetString(ByteBuffer), @"(?=\w\w\w)([\w, ]+)").Trim();
            }
            else {
                return br.ReadString();
            }
        }
    }

    private static string matchRegex(string txt, string rgx) {
        Regex r;
        Match m;
        try {
            r = new Regex(rgx, RegexOptions.IgnoreCase);
            m = r.Match(txt);
            if (m.Success) {
                return m.Groups[1].Value.ToString();
            }
            else {
                return "";
            }
        }
        catch {
            return "";
        }
    }

我们使用的是 excel 2003 和 excel 2007+ 文件格式(.xls 和 .xlsx)。对于 .xls 我必须查看 .xls 文件本身。对于 .xlsx,锁定用户存储在 ~$ temp 文件中。我知道,对于 .xls 文件,它是脏代码,但我不知道 .xls 文件格式的结构。因此,我只是读取了一堆包含 ascii 用户名的字节,然后执行一个正则表达式来提取该用户名。

4

2 回答 2

8

它将用户名存储在 xlsx 文件中

不,不是 .xlsx 文件。Excel 创建另一个文件来存储用户名。它启用了隐藏文件属性,因此您通常无法使用资源管理器看到它。

它通常与原始文件同名,但前缀为~$. 所以对于一个名为的文件test.xlsx,你会得到一个名为~$test.xlsx. 它是一个二进制文件,包含在默认代码页和 utf-16 中编码的用户名。一个十六进制转储显示它的样子:

0000000000: 0C 48 61 6E 73 20 50 61 │ 73 73 61 6E 74 20 20 20  ♀Hans Passant
0000000010: 20 20 20 20 20 20 20 20 │ 20 20 20 20 20 20 20 20
0000000020: 20 20 20 20 20 20 20 20 │ 20 20 20 20 20 20 20 20
0000000030: 20 20 20 20 20 20 20 0C │ 00 48 00 61 00 6E 00 73         ♀ H a n s
0000000040: 00 20 00 50 00 61 00 73 │ 00 73 00 61 00 6E 00 74     P a s s a n t
0000000050: 00 20 00 20 00 20 00 20 │ 00 20 00 20 00 20 00 20
0000000060: 00 20 00 20 00 20 00 20 │ 00 20 00 20 00 20 00 20
0000000070: 00 20 00 20 00 20 00 20 │ 00 20 00 20 00 20 00 20
0000000080: 00 20 00 20 00 20 00 20 │ 00 20 00 20 00 20 00 20
0000000090: 00 20 00 20 00 20 00 20 │ 00 20 00 20 00 20 00 20
00000000A0: 00 20 00 20 00          │

文件中奇怪的 0x0C 字是以字符(不是字节)为单位的字符串长度,后跟 54 个字符用于存储用户名,并用空格填充。最简单的读取方法是使用 BinaryReader.ReadString():

public static string GetExcelFileOwner(string path) {
    string uspath = Path.Combine(Path.GetDirectoryName(path), "~$" + Path.GetFileName(path));
    if (!File.Exists(uspath)) return "";
    var sharing = FileShare.ReadWrite | FileShare.Delete;
    using (var fs = new FileStream(uspath, FileMode.Open, FileAccess.Read, sharing))
    using (var br = new BinaryReader(fs, Encoding.Default)) {
        return br.ReadString();
    }
}

但不一定是最正确的方法,如果 8 位编码在您的语言环境中无法正常工作,您可能需要改进代码并尝试定位 utf-16 字符串(不使用 ReadString)。Seek() 首先偏移 0x37。确保正确使用该方法,它具有隐式竞争条件,因此请确保仅在操作失败使用它,并且无论如何都期望返回空字符串。我不能保证此方法在所有 Excel 版本(包括未来版本)上都能正常工作,我只在工作站级机器上测试了 Office 2013。

于 2014-09-29T11:08:34.680 回答
0

你哪部分有问题?

在不了解 xslx 的情况下,我只能猜测:您想打开文件并指定 FileAccess.Read 和 FileShare.ReadWrite,如下所示:How to read open excel file at C# after that, you use some kind of library to turn XSLX into DataTable,并提取您需要的特定行。

于 2014-09-26T10:50:07.290 回答