Здраствуйте. Дело вот в чем. Необходимо Получить данные из работающего приложения Excel.
На Вход подаются:
Worksheet — объект(или ссылка) книги MS Excel
Name — имя в виде строки , заданное для одного или нескольких объектов Excel::Range, содержащих данные.
Конечно получить объект Excel::Name достаточно просто.
Проблема состоит в следующем этапе. Ну никак не получается получить эту самую коллекцию объектов Range, с которыми связано это имя (в коде выше "TestName").Точнее не работает вот что
Excel::RangePtr range = spTheName->GetRefersToRange();
Если имя указывает на один Range, то вроде бы все нормально, но вот если на несколько, то это уже проблема. Не выходит. А больно надо, так как данные как я понимаю можно достать только через Range.
Пожалуйста, подскажите что может быть здесь за причина и что можно предпринять. Может я чего не так делаю?
Заранее благодарен.
Лень — это не врожденное чувство советского человека, а средство борьбы с неуемной, но бестолковой энергией начальника...
Здравствуйте, morok, Вы писали:
M>Проблема состоит в следующем этапе. Ну никак не получается получить эту самую коллекцию объектов Range, с которыми связано это имя (в коде выше "TestName").Точнее не работает вот что
M>
M>Excel::RangePtr range = spTheName->GetRefersToRange();
M>
M>Если имя указывает на один Range, то вроде бы все нормально, но вот если на несколько, то это уже проблема. Не выходит.
Да вроде это и на VBA так не работает, попробуйте проще, типа
RangePtr range = pXL->GetRange(_variant_t("TestName"));
Здравствуйте, Elena_, Вы писали:
E_>Да вроде это и на VBA так не работает, попробуйте проще, типа E_>
E_>RangePtr range = pXL->GetRange(_variant_t("TestName"));
E_>
Да действительно помогло. Проблема решилась. Спасибо.
А вы немогли бы еще подсказать, почему не работает вот этот код:
NamePtr name = spTheWorkbook->GetNames()->Add();
//Пробовал делать вот так...
//NamesPtr names = spTheWorkbook->GetNames();
//NamePtr name = names->Add( _variant_t("TestName") );
//И вот так...
//NamePtr name = m_spExcelApp->GetNames()->Add( _variant_t("TestName") );
Генерируется исключение при вызове функции Add объекта Names — CXX0030: Error: expression cannot be evaluated
Опять же заранее спасибо
Лень — это не врожденное чувство советского человека, а средство борьбы с неуемной, но бестолковой энергией начальника...
Здравствуйте, morok, Вы писали:
M>почему не работает вот этот код: M>
M>NamePtr name = spTheWorkbook->GetNames()->Add();
M>//Пробовал делать вот так...
M>//NamesPtr names = spTheWorkbook->GetNames();
M>//NamePtr name = names->Add( _variant_t("TestName") );
M>//И вот так...
M>//NamePtr name = m_spExcelApp->GetNames()->Add( _variant_t("TestName") );
M>
M>Генерируется исключение при вызове функции Add объекта Names — CXX0030: Error: expression cannot be evaluated
Хотя все параметры формально Optional, на самом деле при отсутствии одних из них должны быть заданы другие — по смыслу
expression Required. An expression that returns a Names object.
Name Optional Variant. Required if NameLocal isn’t specified. The text to use as the name (in the language of the macro). Names cannot include spaces and cannot look like cell references.
RefersTo Optional Variant. Required unless one of the other RefersTo arguments is specified. Describes what the name refers to (in the language of the macro, using A1-style notation). Note Nothing is returned if the reference does not exist.
...
Понятно, что если добавляем имя, то как минимум должно быть указано само имя и то, на что оно указывает.
Я конечно понимаю, что наверное выгляжу глупо. Но если так, то уж пожалуйста ткните меня еще раз...
Вот код, который благодаря вам почти работает.
Excel::SheetsPtr spSheets = spTheWorkbook->GetSheets();
Excel::_WorksheetPtr spWorksheet = spSheets->GetItem(1);
//Получаем объединение
RangePtr r1 = spWorksheet->GetRange( "A1:C2" );
RangePtr r2 = spWorksheet->GetRange( "D3:D8" );
RangePtr unionRange = m_spExcelApp->Union( r1, r2 );
//Добавляем новое имя , указывающее на созданное объединение, причем адрес получаем External
NamePtr name = spTheWorkbook->GetNames()->Add(
_variant_t("TestName"),
_variant_t(
unionRange->GetAddress( _variant_t(true), _variant_t(true),Excel::xlA1, _variant_t(true) )
)
);
//А вот при попытке получить этот объект с которым связано имя программа вылетает
//с неизвестным исключением при попытке вызова метода GetRange
//ни один из представленных ниже вариантов не работает
//NamePtr foundName = spTheWorkbook->GetNames()->Item( _bstr_t("TestName") );
//RangePtr foundRange = m_spExcelApp->GetRange( foundName->GetRefersTo() );
//or
//RangePtr foundRange = m_spExcelApp->GetRange( _variant_t("TestName") );
//or
RangePtr foundRange = spWorksheet->GetRange( _variant_t("TestName") );
//Это для теста
foundRange->GetInterior()->ColorIndex = 5;
Пробовал указать привязку имени разными способами, то есть
был и такой
Здравствуйте, morok, Вы писали:
M>Я конечно понимаю, что наверное выгляжу глупо. Но если так, то уж пожалуйста ткните меня еще раз...
M>Вот код, который благодаря вам почти работает.
M>
M>Excel::SheetsPtr spSheets = spTheWorkbook->GetSheets();
M>Excel::_WorksheetPtr spWorksheet = spSheets->GetItem(1);
M>//Получаем объединение
M>RangePtr r1 = spWorksheet->GetRange( "A1:C2" );
M>RangePtr r2 = spWorksheet->GetRange( "D3:D8" );
M>RangePtr unionRange = m_spExcelApp->Union( r1, r2 );
M>//Добавляем новое имя , указывающее на созданное объединение, причем адрес получаем External
M>NamePtr name = spTheWorkbook->GetNames()->Add(
M> _variant_t("TestName"),
M> _variant_t(
M> unionRange->GetAddress( _variant_t(true), _variant_t(true),Excel::xlA1, _variant_t(true) )
M> )
M> );
M>//А вот при попытке получить этот объект с которым связано имя программа вылетает
M>//с неизвестным исключением при попытке вызова метода GetRange
M>//ни один из представленных ниже вариантов не работает
M>//NamePtr foundName = spTheWorkbook->GetNames()->Item( _bstr_t("TestName") );
M>//RangePtr foundRange = m_spExcelApp->GetRange( foundName->GetRefersTo() );
M>//or
M>//RangePtr foundRange = m_spExcelApp->GetRange( _variant_t("TestName") );
M>//or
M>RangePtr foundRange = spWorksheet->GetRange( _variant_t("TestName") );
M>//Это для теста
M>foundRange->GetInterior()->ColorIndex = 5;
M>
M>Пробовал указать привязку имени разными способами, то есть M>был и такой M>
M>и т.п.
M>Ну ничего не могу понять, да и в хэлпе по VBA толком про такие вещи ничего не написано. M>Помогите, чем сможите.
M>Спасибо.
Сам нашел причину.
Дело вот в чем. При вожу код на VBA
Dim newName As Name
Dim foundName As Name
Dim r As range
Dim r1 As range
Dim r2 As range
Dim ur As range
Dim wb As Workbook
Dim str As String'Create two ranges and then union themSet r1 = range("a1", "a2")
Set r2 = range("a1", "b2")
Set ur = Union(r1, r2)
'Create new Name object and bind it with the range unionSet newName = Application.Names.Add("TestName", ur.Address(True, True, xlA1, True))
'Get name object from the applicationSet foundName = Application.Names.Item("TestName")
str = Replace(foundName.RefersTo, Chr(34), "")
'Get range from nameSet r = Application.range(str)
r.Interior.ColorIndex = 5
Дело в том, что значение строковой переменной ReferTo объекта Name содержит ссылку на адрес в виде, например: "="[Книга1]Лист1!$A$1""
А для получения Range'a необходиом подставить строку вида "=[Книга1]Лист1!$A$1"
Заметили различия?
Тот кто назвал язык VBA — языком кухарок был не прав.
Лень — это не врожденное чувство советского человека, а средство борьбы с неуемной, но бестолковой энергией начальника...
Здравствуйте, morok, Вы писали:
M>Дело вот в чем. При вожу код на VBA
M>
M>'Create two ranges and then union them
M>Set r1 = range("a1", "a2")
M>Set r2 = range("a1", "b2")
M>Set ur = Union(r1, r2)
M>'Create new Name object and bind it with the range union
M>Set newName = Application.Names.Add("TestName", ur.Address(True, True, xlA1, True))
M>'Get name object from the application
M>Set foundName = Application.Names.Item("TestName")
M>str = Replace(foundName.RefersTo, Chr(34), "")
M>'Get range from name
M>Set r = Application.range(str)
M>r.Interior.ColorIndex = 5
M>
потому что если устанавливать вручную имя для несвязанного диапазона, то лист повторяется для каждого диапазона, наверное, Excel'ю "так понятнее". Точка с запятой в Russian locale, в English — запятая, это можно через Internatonal узнавать в произвольном случае
Здравствуйте, Elena_, Вы писали:
E_>Здравствуйте, morok, Вы писали:
M>>Дело вот в чем. При вожу код на VBA
M>>
M>>'Create two ranges and then union them
M>>Set r1 = range("a1", "a2")
M>>Set r2 = range("a1", "b2")
M>>Set ur = Union(r1, r2)
M>>'Create new Name object and bind it with the range union
M>>Set newName = Application.Names.Add("TestName", ur.Address(True, True, xlA1, True))
M>>'Get name object from the application
M>>Set foundName = Application.Names.Item("TestName")
M>>str = Replace(foundName.RefersTo, Chr(34), "")
M>>'Get range from name
M>>Set r = Application.range(str)
M>>r.Interior.ColorIndex = 5
M>>
E_>Можно, наверное, и так, но я бы сделала типа E_>
E_>потому что если устанавливать вручную имя для несвязанного диапазона, то лист повторяется для каждого диапазона, наверное, Excel'ю "так понятнее". Точка с запятой в Russian locale, в English — запятая, это можно через Internatonal узнавать в произвольном случае
Все хорошо. Это идея хорошая
если устанавливать вручную имя для несвязанного диапазона, то лист повторяется для каждого диапазона, наверное, Excel'ю "так понятнее".
Но вот только проблема в том, что при создании нового имени необходимо чтобы оно было таким:
"="[Книга1]Лист1!$A$1:$B$1""
Причем наличие кавычек похоже является критическим. Я только что проверил. Приведенная вами конструкция не хочет работать.
_bstr_t sAddr1 = r1->GetAddress(_variant_t(true), _variant_t(true),Excel::xlA1, _variant_t(true) );
_bstr_t sAddr2 = r2->GetAddress(_variant_t(true), _variant_t(true),Excel::xlA1, _variant_t(true) );
_bstr_t sAddr = _bstr_t("=") + sAddr1 + _bstr_t(";") + sAddr2;
//-------А Вот на этом месте и вылетает,но если получить строку таким образом, то все нормально
//_bstr_t sAddr = _bstr_t("=\"") + sAddr1 + _bstr_t(";") + sAddr2 + _bstr_t("\"");
NamePtr name = pBook->GetNames()->Add(_variant_t("TestName"), sAddr);
//------------------------------------------------------------------
RangePtr foundRange = pXL->GetRange( _variant_t("TestName") );
Лень — это не врожденное чувство советского человека, а средство борьбы с неуемной, но бестолковой энергией начальника...
Здравствуйте, Elena_, Вы писали:
E_>Здравствуйте, morok, Вы писали:
M>>Дело вот в чем. При вожу код на VBA
M>>
M>>'Create two ranges and then union them
M>>Set r1 = range("a1", "a2")
M>>Set r2 = range("a1", "b2")
M>>Set ur = Union(r1, r2)
M>>'Create new Name object and bind it with the range union
M>>Set newName = Application.Names.Add("TestName", ur.Address(True, True, xlA1, True))
M>>'Get name object from the application
M>>Set foundName = Application.Names.Item("TestName")
M>>str = Replace(foundName.RefersTo, Chr(34), "")
M>>'Get range from name
M>>Set r = Application.range(str)
M>>r.Interior.ColorIndex = 5
M>>
E_>Можно, наверное, и так, но я бы сделала типа E_>
E_>потому что если устанавливать вручную имя для несвязанного диапазона, то лист повторяется для каждого диапазона, наверное, Excel'ю "так понятнее". Точка с запятой в Russian locale, в English — запятая, это можно через Internatonal узнавать в произвольном случае
Лень — это не врожденное чувство советского человека, а средство борьбы с неуемной, но бестолковой энергией начальника...
Однако вышеприведенная строка тоже не будет работать по той же причине — из-за ковычек
То есть объект Name возвращает ссылку через RefersTo в виде "="[Книга1]Лист1!$A$1:$A$3""
А для использования этой строки в получении Range'a необходимо передать строку в виде "=[Книга1]Лист1!$A$1:$A$3"
или "[Книга1]Лист1!$A$1:$A$3"
— символ " — обозначает строку
Вот такая вот ф...я выходит.
В общем достаточно неудобная вещь. Получается нельзя напрямую использовать результат одной функции(Name.RefersTo()) как аргумент другой функции(Application.GetRange(Name.RefersTo()), сначала необходимо результат преобразовать, а потом уже использовать. Мне кажется это какая-то недоработка компании MS. Хотя может я просто не в курсе чего-то.
Лень — это не врожденное чувство советского человека, а средство борьбы с неуемной, но бестолковой энергией начальника...
Здравствуйте, morok, Вы писали:
M>В общем достаточно неудобная вещь. Получается нельзя напрямую использовать результат одной функции(Name.RefersTo()) как аргумент другой функции(Application.GetRange(Name.RefersTo()), сначала необходимо результат преобразовать, а потом уже использовать. Мне кажется это какая-то недоработка компании MS. Хотя может я просто не в курсе чего-то.
Мне надо еще немного посмотреть, но мне всегда казалось, что, например, проблемы на несвязанных диапазонах с RefersToRange возникают именно в Russian версии, именно из-за чехарды с разделителями диапазонов — , или ; Сейчас под рукой нет English версии, чтобы проверить, думаю, что в ней не должно быть проблем с несвязанными диапазонами?
Этот вариант я проверила на Excel XP — Russian — вроде бы работало, похоже, что зависит от версии Excel'я, где-то еще и кавычки всплывают
Вообще-то не замечала, что с кавычками тоже проблемы, надо покопать, с запятой и точкой с запятой точно все время вылезают коллизии. Недавно попадалось здесь
Здравствуйте, Elena_, Вы писали:
E_>Вообще-то не замечала, что с кавычками тоже проблемы
Наконец-то до меня дошло, что значит эта история с кавычками
Дело в том, что Excel позволяет создавать имена не только для диапазонов, но и для констант, например,
ThisWorkbook.Names.Add"PI", "=3.1415"
При этом имя PI не появится ни в списке имен слева от строки формул, ни в списке имен в диалоге Перейти (Правка->Перейти), оно будет только в общем списке имен (Вставка->Имя), так как оно не относится ни к какому диапазону, при этом его можно использовать в формулах, например,
=2*PI*10
Также можно создать имя и для строковой константы, например
ThisWorkbook.Names.Add"Developer", "=""Elena"""
Это имя Developer тоже не появится ни в списке имен слева от строки формул, ни не в списке имен в диалоге Перейти, так оно также не относится ни к какому диапазону, но его можно использовать в формулах типа
="Разработчик: " & Developer
Поэтому, когда Вы добавляете имя, заключая ссылку на диапазон в кавычки, типа
ThisWorkbook.Names.Add"Addr1", "=""Лист1!$D$17"""
то Вы фактически добавляете строковую константу, не имеющую отношения ни к какому диапазону, и просто по содержанию "похожую" на ссылку, а могло быть не "=""Лист1!$D$17""", а "=""Лист1!$ZZZZZ$177777""", и все бы прекрасно отработало
Чтобы убедиться в этом, попробуйте выбрать Правка->Перейти на листе Лист1 и посмотреть, позволяет ли имя, добавленное с кавычками, перейти к диапазону, на который оно вроде бы ссылается, а на самом деле это не ссылка, а просто текстовая константа, содержащая ссылку.
Естественно, чтобы получить диапазон, используя такое имя, нужно выполнить обратное преобразование, то есть константу преобразовать к ссылке, как раз то, о чем Вы говорили
M>Получается нельзя напрямую использовать результат одной функции(Name.RefersTo()) как аргумент другой функции(Application.GetRange(Name.RefersTo()), сначала необходимо результат преобразовать, а потом уже использовать.
Конечно, это не то, что должно быть
Когда же имя создается с использованием именно ссылки, а не строковой константы,
ThisWorkbook.Names.Add"Addr", "=Лист333!$D$17"
тогда оно появляется и в списке имен слева от строки формул, и в диалоге Перейти, и выбрав его, там или там, Вы сразу переходите к диапазону, соответственно, и в коде дополнительного преобразования не требуется
Если же, как я поняла, у Вас возникают проблемы именно с методом Add, когда Вы добавляете именно ссылку, а не строковую константу, попробуйте записать макрос при выполнении действия присвоения имени, и посмотрите, какой синтаксис требует Ваша версия Excel. Попробуйте выполнить этот макрос уже как макрос в Excel, а потом перенесите в С++. Как я уже говорила, у меня все проблемы бывают с вариациями запятых и точек с запятой
Здравствуйте, Elena_, Вы писали:
E_>Здравствуйте, Elena_, Вы писали:
E_>>Вообще-то не замечала, что с кавычками тоже проблемы
E_>Наконец-то до меня дошло, что значит эта история с кавычками
E_>Дело в том, что Excel позволяет создавать имена не только для диапазонов, но и для констант, например, E_>
E_>ThisWorkbook.Names.Add"PI", "=3.1415"
E_>
E_>При этом имя PI не появится ни в списке имен слева от строки формул, ни в списке имен в диалоге Перейти (Правка->Перейти), оно будет только в общем списке имен (Вставка->Имя), так как оно не относится ни к какому диапазону, при этом его можно использовать в формулах, например, E_>
E_>=2*PI*10
E_>
E_>Также можно создать имя и для строковой константы, например E_>
E_>Это имя Developer тоже не появится ни в списке имен слева от строки формул, ни не в списке имен в диалоге Перейти, так оно также не относится ни к какому диапазону, но его можно использовать в формулах типа E_>
E_>="Разработчик: " & Developer
E_>
E_>Поэтому, когда Вы добавляете имя, заключая ссылку на диапазон в кавычки, типа E_>
E_>то Вы фактически добавляете строковую константу, не имеющую отношения ни к какому диапазону, и просто по содержанию "похожую" на ссылку, а могло быть не "=""Лист1!$D$17""", а "=""Лист1!$ZZZZZ$177777""", и все бы прекрасно отработало E_>Чтобы убедиться в этом, попробуйте выбрать Правка->Перейти на листе Лист1 и посмотреть, позволяет ли имя, добавленное с кавычками, перейти к диапазону, на который оно вроде бы ссылается, а на самом деле это не ссылка, а просто текстовая константа, содержащая ссылку.
E_>Естественно, чтобы получить диапазон, используя такое имя, нужно выполнить обратное преобразование, то есть константу преобразовать к ссылке, как раз то, о чем Вы говорили
M>>Получается нельзя напрямую использовать результат одной функции(Name.RefersTo()) как аргумент другой функции(Application.GetRange(Name.RefersTo()), сначала необходимо результат преобразовать, а потом уже использовать.
E_>Конечно, это не то, что должно быть
E_>Когда же имя создается с использованием именно ссылки, а не строковой константы, E_>
E_>тогда оно появляется и в списке имен слева от строки формул, и в диалоге Перейти, и выбрав его, там или там, Вы сразу переходите к диапазону, соответственно, и в коде дополнительного преобразования не требуется
E_>Если же, как я поняла, у Вас возникают проблемы именно с методом Add, когда Вы добавляете именно ссылку, а не строковую константу, попробуйте записать макрос при выполнении действия присвоения имени, и посмотрите, какой синтаксис требует Ваша версия Excel. Попробуйте выполнить этот макрос уже как макрос в Excel, а потом перенесите в С++. Как я уже говорила, у меня все проблемы бывают с вариациями запятых и точек с запятой
Да действительно так. Большое спасибо за помощь в данном вопросе.
Лень — это не врожденное чувство советского человека, а средство борьбы с неуемной, но бестолковой энергией начальника...
Вы как-то писали, что разделитель в записи вида
"=A1:A2,D2:D4"
зависит от локали. А немогли бы подсказать что это именно за разделитель. То есть разделитель элементов списка или что-то другое.
Потому как выражение вида
Application.Internation(xlListSeparator)
должно возвращать именно этот разделитель?
Заранее спасибо.
Лень — это не врожденное чувство советского человека, а средство борьбы с неуемной, но бестолковой энергией начальника...
Здравствуйте, morok, Вы писали:
M>Вы как-то писали, что разделитель в записи вида M>"=A1:A2,D2:D4" M>зависит от локали. А немогли бы подсказать что это именно за разделитель. То есть разделитель элементов списка или что-то другое.
M>Потому как выражение вида M>
M>Application.Internation(xlListSeparator)
M>
M>должно возвращать именно этот разделитель?
Честно говоря, в документации я ни разу не встречала четкой формулировки по этому поводу, но вроде используют именно xlListSeparator в таких адресах, есть еще xlColumnSeparator и xlRowSeparator — это для массивов
В инете выложена такая книга TranslateIT.zip, там преобразуют всякие формулы в разные локализации Excel, там можно посмотреть, когда они используют xlListSeparator, а когда xlColumn и Row Separator, код в ней открыт