Программно получить пользователя, который блокирует книгу 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 и извлеките конкретную строку, которая вам нужна.