Получение таблицы Excel из буфера обмена в виде Xml

Дата публикации: 11.10.2010
Дата изменения:  14.06.2011 (добавил примечание)
Состояние: завершена

Задача:
    Из буфера обмена получить таблицу MS Excel, причём не просто в виде xml-строки, а сразу в виде XML-объекта, в котором упакована эта строка.

Примечание:
    В изложенном ниже решении рассматривается только отлов команды _PASTECLIP, но вставка может быть выполнена и др. командами, такими как: _pasteblock, _pasteorig, _pastespec и т.д. (увидеть полный перечень команд можно заглянув в меню Edit). Соответственно, чтобы отследить любую попытку вставки чего-либо через буфер обмен - следует мониторить сразу группу команд.

Решение (проверен под MS Excel 2003-2010):

   1:  //Код в классе, реализующем интерфейс IExtensionApplication
   2:  ...
   3:          public void Initialize()
   4:          {
   5:              //Подписываюсь на нужные события
   6:              EventsRegistration();
   7:          }
   8:   
   9:          private void EventsRegistration()
  10:          {
  11:              //Активация документа - событие происходит при открытии, создании или установке текущим чертежа.
  12:              dm.DocumentActivated += new DocumentCollectionEventHandler(dm_DocumentActivated);
  13:              //Отлавливаем вызовы интересующих нас команд
  14:              Drawing.CommandWillStart += new CommandEventHandler(Drawing_CommandWillStart);
  15:          }
  16:   
  17:          void Drawing_CommandWillStart(object sender, CommandEventArgs e)
  18:          {
  19:              //Если вызвана команда вставки из буфера обмена, а в буфере обмена содержится таблица Excel, то...
  20:              if (e.GlobalCommandName.ToUpper() == "_PASTECLIP" && Clipboard.ContainsData("XML Spreadsheet"))
  21:              {
  22:                  try
  23:                  {
  24:                      string result = string.Empty;
  25:                      //Извлекаем данные из буфера обмена, преобразовывая их в формат XML
  26:                      using (MemoryStream ms = (MemoryStream)Clipboard.GetData("XML Spreadsheet"))
  27:                      {
  28:                          Byte[] b = new Byte[ms.Length];
  29:                          ms.Read(b, 0, (int)ms.Length);
  30:                          //ms.Position = 0;
  31:                          
  32:                          //Результат преобразований
  33:                          result = System.Text.UTF7Encoding.UTF8.GetString(b);
  34:                          ms.Close();
  35:                          //Удаляю "левые" невидимые символы в конце строки (обычным 'Trim' они не убираются)...
  36:                          result = result.Substring(0, result.LastIndexOf('>')+1).Trim();                        
  37:                      }
  38:                      //На основе полученной строки создаём XElement
  39:                      XElement xml = XElement.Parse(result);
  40:                      //... Далее идёт нужный нам код
  41:                  }
  42:                  catch (System.Exception ex)
  43:                  {
  44:                      ed.WriteMessage(ex.Message);//ed -> экземпляр класса Database
  45:                  }
  46:   
  47:              }
  48:          }
  49:  ...

Внимание!
    Особое внимание на строку 36 - её наличие обязательно! Без неё велика вероятность того, что в строке 39 можно поймать Exception - виной тому некие "невидимые символы", которые, как оказалось зачастую имеются в конце строки, при этом не удаляются командой Trim и, как следствие - приводят к возникновению ошибки. По какой причине они порой добавляются - я не знаю (предполагаю, что это ошибка в стандартной .net-библиотеке парсинга XML, хотя не утверждаю это) - возможность появления такой ситуации следует учитывать.

Внимание 2!
Я работаю на виртуальной машине, находящейся на удалённо расположенном виртуальном сервере (на ней и MS VS 2010 и AutoCAD 2009). В процессе решения данной задачи выявил следующие интересные моменты:
  1. Если работать через консоль VMware, то в строке 20, условие Clipboard.ContainsData("XML Spreadsheet") не будет возвращать значение "True", несмотря на то, что на самом деле в буфере обмена содержится именно таблица MS Excel.
  2. Если  не завершая отладки (дабы понять причину проблемы), закрыть окно консоли VMware и зайти на машину через терминальную консоль (удалённый рабочий стол) и так же попытаться вставить всё то же содержимое буфера обмена, то в этом случае условие lipboard.ContainsData("XML Spreadsheet") вернёт нам то, что нужно, т.е. - "True"! 
  3. Если теперь закрыть окно терминальной консоли и снова войти под консолью VMware и опять же попробовать вставить содержимое буфера обмена в AutoCAD - снова получим результат, описанный в п.1! 
Вывод:
    Консоль VMware криво работает с буфером обмена - и об этом следует помнить, в том случае когда работа ведётся на удалённо расположенной машине, с использованием данного инструмента.


    Однако есть минусы и при работе через терминальную консоль - если в AutoCAD выделить примитив ручками и попытаться его перетащить, то начинаются жуткие тормоза - курсор движется слайдами... Этого недостатка лишена консоль VMware. Так что в каком случае через какую консоль следует работать - нужно выбирать исходя из тех задач, которые должны выполняться в рамках текущей сессии работы.

Comments