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

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

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

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

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

Теперь я хочу включить в уведомление имя пользователя, которое блокирует файл Excel. Я искал msdn, форум netoffice и так далее... и не нашел решения. Я знаю, что если вы откроете файл excel для чтения, он сохранит имя пользователя в файле xlsx. Как я могу получить доступ к этой конкретной части информации через С#?

РЕДАКТИРОВАТЬ: я сделал это:

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 пользователь блокировки хранится во временном файле ~$. Я знаю, что для файла .xls это грязный код, но я понятия не имею, как устроен формат файла .xls. Поэтому я просто читаю кучу байтов, которые включают имя пользователя ascii, и делаю регулярное выражение для извлечения этого имени пользователя.


person nire    schedule 26.09.2014    source источник
comment
вы пробовали смотреть на Win32_ConnectionShare и WMIC?   -  person Mike Miller    schedule 29.09.2014


Ответы (2)


он сохранит имя пользователя в файле 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();
    }
}

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

person Hans Passant    schedule 29.09.2014
comment
Привет Ганс. Кажется, это работает только для локальных файлов, а не для файлов Excel, расположенных в сети. Я вижу файл ~$, когда открываю и изменяю файл Excel на жестком диске. Я не вижу файл при открытии из сети. Я проверил файл xlsx, когда открыл его для чтения/записи в сети. Имя пользователя, открывшего его, хранится в файле xlsx. Поэтому я все еще думаю, что мне следует прочитать настройку из файла xlsx. - person nire; 29.09.2014
comment
Хм, нет, довольно сомнительно, что он собирается изменить файл .xlsx только для записи имени пользователя. Единственная проблема, о которой я могу думать, это то, что у пользователя нет прав на запись в общую папку. Но давайте не будем гадать, используйте Process Monitor SysInternals, чтобы увидеть, что происходит, вы увидите, как Excel.exe возится с файлом (файлами). - person Hans Passant; 29.09.2014
comment
Ганс, ты прав. Procmon показывает, что Excel.exe обращается к ~$......xlsx с результатом УСПЕШНО. О боже, и теперь я вижу файл ~$....xlsx на сервере. Я также пробовал файл xls, для которого не создается файл ~$. Имя пользователя хранится в фактическом .xls. Я попробую ваш код и сообщу. - person nire; 29.09.2014
comment
Ганс, ваше решение прекрасно работает для .xlsx! Баунти скоро придет к вам - person nire; 29.09.2014

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

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

person Erti-Chris Eelmaa    schedule 26.09.2014
comment
Я использую сборки netoffice .NET Wrapper для управления файлами и приложениями Office. Проблема в том, что у меня нет возможности извлечь имя пользователя, который (б) блокирует файл excel. - person nire; 26.09.2014
comment
Ну, похоже, вам нужно знать, кто открыл конкретный файл и ФЛАГИ, которые он имеет для этого файла. Утилита PSFile может сделать это через командную строку. psfile \\ваш удаленный ресурс C:\test.xlsx. Если вам интересно, как это сделать программно, вы можете скачать APIMonitor и посмотреть, как это делает psfile. Я лично сомневаюсь, что вы можете получить доступ к имени пользователя в файле xlsx, для меня это звучит как внутреннее дело. - person Erti-Chris Eelmaa; 26.09.2014
comment
Хотя предложенное мной решение требует прав администратора, чего нет-нет. Вы можете использовать монитор API, чтобы проверить, как Excel получает это имя пользователя из удаленного файла. (Например, через какой API вызывается и является ли он внутренним) - person Erti-Chris Eelmaa; 26.09.2014
comment
Я понятия не имею, как это сделать. Я скачал API-монитор из rohitab и получил трассировку процесса excel. Как я могу получить часть информации, которая мне нужна? - person nire; 26.09.2014