Программно получить пользователя, который блокирует книгу Excel

Я использую C# framework 4.5, netoffice 1.6 и sharpdevelop 4.4.1 для манипулирования книгой Excel, расположенной на общем сетевом ресурсе, из Outlook.

В какой-то момент мне нужно изменить доступ к файлу объекта книги (ewb) на readwrite следующим образом:

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

Прежде чем изменить доступ к файлу, я проверяю, заблокирован ли файл на сервере. Если файл заблокирован, я уведомлю пользователя, чтобы повторить действие позже.

Теперь я хочу включить имя пользователя, которое блокирует файл Excel, в уведомление. Я искал msdn, netoffice forum, и так далее... и не нашел решения. Я знаю, что если вы откроете файл Excel readwrite, он сохранит имя пользователя в файле 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, и делаю регулярное выражение для извлечения этого имени пользователя.

2 ответа

Решение

он будет хранить имя пользователя в файле xlsx

Нет, не в файле.xlsx. Excel создает другой файл для хранения имени пользователя. Он имеет атрибут Hidden file, поэтому вы не можете видеть его в Проводнике.

Обычно оно имеет то же имя, что и исходный файл, но с префиксом ~$, Так что для файла с именем 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();
    }
}

Но не обязательно самым правильным способом, вы можете улучшить код и попытаться найти строку utf-16 (не с ReadString), если 8-битные кодировки не работают в вашей локали. Сначала искать () для смещения 0x37. Обязательно используйте метод правильно, у него есть неявное условие гонки, поэтому убедитесь, что вы используете его только после сбоя операции и ожидаете, что пустая строка все равно вернется. Я не могу гарантировать, что этот метод будет работать корректно на всех версиях Excel, включая будущие, я тестировал только для Office 2013 на компьютере класса рабочей станции.

С какой частью у вас проблемы?

Не зная ничего о xslx, я могу только догадываться, что: вы хотите открыть файл и указать FileAccess.Read & FileShare.ReadWrite как здесь: Как читать открытый файл Excel в C# после этого, вы используете какую-то библиотеку, чтобы превратить XSLX в DataTable и извлеките конкретную строку, которая вам нужна.

Другие вопросы по тегам