Главная
Новости, анонсы ...


 Опыты
Статьи, исходники
и прочее





Чернопятов Е.А. Автоматизация приложений MS Office. Вопросы и Ответы.

 

В данной части собраны материалы, которые можно условно назвать "Вопросы и ответы". В процессе обсуждения проблем автоматизации часто всплывают технические вопросы, ответы на которые можно дать вкратце, и для которых нет смысла писать новую статью. Эта часть, как я думаю, будет периодически обновляться по мере поступления новых вопросов.

ВНИМАНИЕ! Данная часть предполагает, что читатель уже ознакомился с предыдущими частями. Здесь НЕ рассматриваются вопросы типа "как создать класс", "как узнать имя метода","откуда берется константа wdPrintAllDocument" и т.п. Кроме того, именование переменных осуществляется в той же манере, что и в предыдущих частях.

Благодарности: Николаю Кисееву за помощь в подготовке данной части.

 


 


Вопрос: Как сделать перебор открытых документов MS Word?

Ответ: Открытые документы находятся в коллекции CWindows. Соответственно, одно открытое окно представлено классом CWindow0. Напомню, что такое странное имя объясняется тем, что ClassWizard автоматически переименовывает импортированные из библиотеки типов классы для того, чтобы избежать коллизий с уже существующими в MFC классами. Перебор можно осуществить следующим способом:

CWindow0 oWin;
CWindows oWins;

oWins = oApp.get_Windows();
for(long i=1; i<=oWins.get_Count(); ++i)
{
	oWin = oWins.Item(COleVariant(i));
	oWin.Activate();
	oDoc = oWin.get_Document();
	oDoc.Activate();
	
	// тут что-то делаем с полученным oDoc (документом)
	// ...................................
	// ...................................
}

 


Вопрос: Как распечатать документ MS Word?

Ответ: Для вывода на печать используется несколько вариантов метода PrintOut  класса CDocument0:

  • void PrintOutOld(VARIANT * Background, VARIANT * Append, VARIANT * Range, VARIANT * OutputFileName, VARIANT * From, VARIANT * To, VARIANT * Item, VARIANT * Copies, VARIANT * Pages, VARIANT * PageType, VARIANT * PrintToFile, VARIANT * Collate, VARIANT * ActivePrinterMacGX, VARIANT * ManualDuplexPrint )

  • void PrintOut2000(VARIANT * Background, VARIANT * Append, VARIANT * Range, VARIANT * OutputFileName, VARIANT * From, VARIANT * To, VARIANT * Item, VARIANT * Copies, VARIANT * Pages, VARIANT * PageType, VARIANT * PrintToFile, VARIANT * Collate, VARIANT * ActivePrinterMacGX, VARIANT * ManualDuplexPrint, VARIANT * PrintZoomColumn, VARIANT * PrintZoomRow, VARIANT * PrintZoomPaperWidth, VARIANT * PrintZoomPaperHeight )

  • void PrintOut(VARIANT * Background, VARIANT * Append, VARIANT * Range, VARIANT * OutputFileName, VARIANT * From, VARIANT * To, VARIANT * Item, VARIANT * Copies, VARIANT * Pages, VARIANT * PageType, VARIANT * PrintToFile, VARIANT * Collate, VARIANT * ActivePrinterMacGX, VARIANT * ManualDuplexPrint, VARIANT * PrintZoomColumn, VARIANT * PrintZoomRow, VARIANT * PrintZoomPaperWidth, VARIANT * PrintZoomPaperHeight )

Методы принимают довольно большое число параметров, но, как обычно, их назначение понятно из их названий.

Пример печати текущего документа:

int  iCopies = 5; // печатаем 5 копий

doc.PrintOutOld(
	// печать в фоновом режиме	
	vTrue,	
	// в данном случае игнорируем. А вот если печатаем в файл, 
	// то этот параметр задает добавление к уже существующему файлу
	covOptional,	
	// диапазон печати. В данном случае wdPrintAllDocument - 
	// печатать весь документ, игнорировать "с(From)..по(To)..", "выбранное",
	// "текущую страницу" и тому подобное
	COleVariant((short)wdPrintAllDocument),	
	// если печатаем в файл - то имя выходного файла	
	COleVariant(""),
	// с какой страницы. В данном случае - игнорируем, так как печатаем весь документ	
	covOptional,
	// по какую страницу. В данном случае - игнорируем, так как печатаем весь документ
	covOptional, 	
	// wdPrintDocumentContent означает, что печатаем содержимое документа
	COleVariant((short)wdPrintDocumentContent), 
	//число копий
	COleVariant((short)iCopy), 
	// строка, задающая, какие именно страницы печатать, 
	// например "1,3,6-10". Так как печатаем все, то передаем пустую строку
	COleVariant(""), 
	// тип страниц для печати. В данном случае все, 
	// а можно задать wdPrintEvenPagesOnly - только четные, 
	// или wdPrintOddPagesOnly - только нечетные
	COleVariant((short)wdPrintAllPages),	
	// false - печать на принтер, true - в файл
	vFalse, 
	// разобрать по копиям
	vTrue, 
	// игнорировать установку ActivePrinterMacGX, так как у нас Word for Windows, 
	// а не Word for Macintosh
	covOptional, 
	// без ручной двусторонеей печати
	vFalse
	);

Вопрос: Необходимо установить для строки свойство "Формат/Абзац/Положение на странице/С новой страницы". Для того чтобы узнать, какими методами возможна реализация функций Word-a, я пользуюсь макросами VBA (записываю макросы и смотрю, какие функции задействованы). Макрос установки свойства в VBA содержит:

With Selection.ParagraphFormat
        .LeftIndent = CentimetersToPoints(0)
        .RightIndent = CentimetersToPoints(0)
        .SpaceBefore = 0
        .SpaceBeforeAuto = False
        .SpaceAfter = 0
        .SpaceAfterAuto = False
        .LineSpacingRule = wdLineSpaceSingle
        .Alignment = wdAlignParagraphLeft
        .WidowControl = True
        .KeepWithNext = False
        .KeepTogether = False
        .PageBreakBefore = True
        .NoLineNumber = False
        .Hyphenation = True
        .FirstLineIndent = CentimetersToPoints(0)
        .OutlineLevel = wdOutlineLevelBodyText
        .CharacterUnitLeftIndent = 0
        .CharacterUnitRightIndent = 0
        .CharacterUnitFirstLineIndent = 0
        .LineUnitBefore = 0
        .LineUnitAfter = 0
End With

Добавляю класс CParagraphFormat и код, в котором воспроизведены все операции из макроса:

	CParagraphFormat oCParFormat;
	oCParFormat = oSel.GetParagraphFormat();
	oCParFormat.put_LeftIndent(0.0);
	oCParFormat.put_RightIndent(0.0);
	oCParFormat.put_SpaceBefore(0.0);
	oCParFormat.put_SpaceBeforeAuto(false);
	oCParFormat.put_SpaceAfter(0.0);
	oCParFormat.put_SpaceAfterAuto(false);
	oCParFormat.put_LineSpacingRule(wdLineSpaceSingle);
	oCParFormat.put_Alignment(wdAlignParagraphLeft);
	oCParFormat.put_WidowControl(true);	
	oCParFormat.put_KeepWithNext(false);			
	oCParFormat.put_KeepTogether(false);
	oCParFormat.put_PageBreakBefore(true);				
	oCParFormat.put_NoLineNumber(false);
	oCParFormat.put_Hyphenation(true);					
	oCParFormat.put_FirstLineIndent(0.0);
	oCParFormat.put_OutlineLevel(wdOutlineLevelBodyText);
	oCParFormat.put_CharacterUnitLeftIndent(0.0);
	oCParFormat.put_CharacterUnitRightIndent(0.0);
	oCParFormat.put_CharacterUnitFirstLineIndent(0.0);
	oCParFormat.put_LineUnitBefore(0.0);
	oCParFormat.put_LineUnitAfter(0.0);

НО! Методы: oCParFormat.put_WidowControl(), oCParFormat.put_PageBreakBefore(), oCParFormat.put_Hyphenation() требуют в качестве параметра "long NewValue", если в качестве параметра им передавать true (как это сделано в VBA), при генерации документа Word выдает ошибку "Параметр задан неверно" (The parameter is incorrect). Что я делаю неверно (прислал Видов К.С.)?

Ответ: Путаница с параметрами возникает из-за того, что в VBA значения булевых переменных представляются не так, как мы к этому привыкли в С++. В VBA "Истина (true)" представляется "-1" (минус единицей), а "Ложь (false)" - "0 " (нулём).

Действительно, если в VBА набрать следующий код:

Dim L As Long 
L = True 
L = False 
L = msoTrue 
L = msoFalse 

и посмотреть в отладчике значения переменной "L" после каждого присваивания, то увидим, что True на самом деле - это "-1". От этого все непонятки.
Тогда становится ясно, почему в С++ код oCParFormat.put_WidowControl(true); вызывает ошибку Word-а "Параметр задан неверно" (The parameter is incorrect).
Метод put_WidowControl описан как void put_WidowControl(long newValue), следовательно, компилятор производит преобразование (как мы и привыкли):

put_WidowControl(true) -> put_WidowControl((long)true) -> put_WidowControl(1L)

что и вызывает справедливый гнев Word-а, поскольку он ожидает либо 0, либо -1 (либо wdUndefined, о чем говорит нам товарищ MSDN ), но уж никак не "1".

Решение: Просто добавить в enums.h строки

// VBA style boolean
#ifndef msoTrue 
	#define msoTrue (-1) 
#endif 
#ifndef msoFalse 
	#define msoFalse (0) 
#endif 

и вызывать oCParFormat. put_WidowControl как oCParFormat.put_WidowControl(msoTrue);


Вопрос: Создаю свой проект, как указано в статьях через импорт библиотеки типов. При компиляции получаю огромное количество ошибок типа:

1>------ Build started: Project: ExcelTest, Configuration: Debug Win32 ------
1>Compiling...
1>ExcelTest.cpp
1>excel.tlh(1595) : error C2371: 'FontPtr' : redefinition; different basic types
1> c:\program files\microsoft visual studio 9.0\vc\include\comdef.h(314) : see declaration of 'FontPtr'
1>excel.tlh(1673) : error C2786: 'BOOL (__stdcall *)(HDC,int,int,int,int)' : invalid operand for __uuidof
1>excel.tlh(1673) : error C2923: '_com_IIID' : 'Rectangle' is not a valid template type argument for parameter '_Interface'
1> c:\program files\microsoft sdks\windows\v6.0a\include\wingdi.h(3667) : see declaration of 'Rectangle'
1>excel.tlh(1673) : error C3203: '_com_IIID' : unspecialized class template can't be used as a template argument for 
template parameter '_IIID', expected a real type
1>excel.tlh(1673) : error C2955: '_com_IIID' : use of class template requires template argument list
1> c:\program files\microsoft visual studio 9.0\vc\include\comip.h(40) : see declaration of '_com_IIID'
1>excel.tlh(1677) : error C2786: 'BOOL (__stdcall *)(HDC,int,int,int,int,int,int,int,int)' : invalid operand for __uuidof
1>excel.tlh(1677) : error C2923: '_com_IIID' : 'Arc' is not a valid template type argument for parameter '_Interface'
1> c:\program files\microsoft sdks\windows\v6.0a\include\wingdi.h(2914) : see declaration of 'Arc'
1>excel.tlh(1677) : error C3203: '_com_IIID' : unspecialized class template can't be used as a template argument for 
template parameter '_IIID', expected a real type
1>excel.tlh(1677) : error C2955: '_com_IIID' : use of class template requires template argument list
1> c:\program files\microsoft visual studio 9.0\vc\include\comip.h(40) : see declaration of '_com_IIID'
1>excel.tlh(1684) : error C2371: 'PicturePtr' : redefinition; different basic types
1> c:\program files\microsoft visual studio 9.0\vc\include\comdef.h(328) : see declaration of 'PicturePtr'
1>excel.tlh(2220) : error C2504: '_IMsoDispObj' : base class undefined
............
и т.д. Что делать? Среда Visual Studio 2005, 2008 или выше.

Ответ: Есть 2 варианта.

  1. Простой. Взять уже существующий старый проект и разрешить проапгрейдить его в момент открытия в студии. Подходит, если уже импортированы все нужные классы и не надо импортировать другие.

  2. Чуть более сложный.
    Создать проект, как указано в статье.
    Импортировать необходимые классы.
    Открыть сгененрированные *.h файлы, типа CApplication.h, CWorkbooks.h и т.д.
    Найти в начале каждого файла строку вида (путь может отличаться):
    #import "C:\\Program Files\\Microsoft Office\\Office14\\EXCEL.EXE"
    Закомментировать ее:
    //#import "C:\\Program Files\\Microsoft Office\\Office14\\EXCEL.EXE"
    Собрать проект. Если на данном этапе всё собралось, то и славненько.

    Если появились ошибки вида:
    1>crange.h(335) : warning C4003: not enough actual parameters for macro 'DialogBoxW'
    1>crange.h(335) : error C2059: syntax error : ','
    откройте соответствующий хедер (в данном случае CRange.h), найдите нужную строку с определением метода DialogBox (в данном случае):
    VARIANT DialogBox()
    {
        VARIANT result;
        InvokeHelper(0xf5, DISPATCH_METHOD, VT_VARIANT, (void*)&result, NULL);
        return result;
    }
    и переименуйте метод, например так: VARIANT DialogBox() ---> VARIANT MyDialogBox(). В дальнейшем обращайтесь к методу по новому имени.
 

 


 



 
Оглавление

Последние изменения от 09.08.2013




e-mail:  Yegor A. Blackheel

Поиск по сайту с помощью Yandex

 www.guestbook.ru - лучший сервер гостевых книг