Вопрос: Как сделать перебор открытых документов 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 варианта.
- Простой. Взять уже существующий старый проект и разрешить проапгрейдить его в
момент открытия в студии. Подходит, если уже импортированы все нужные классы и
не надо импортировать другие.
- Чуть более сложный.
Создать проект, как указано в статье.
Импортировать необходимые классы.
Открыть сгененрированные *.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().
В дальнейшем обращайтесь к методу по новому имени.