Mawrat 13094 / 5875 / 1706 Регистрация: 19.09.2009 Сообщений: 8,808 |
||||||||
01.09.2013, 05:11 |
6 |
|||||||
мне нужно чтоб программа работала уже в готовом открытом приложении , а не создавала сама Тогда понадобится не запускать новый экземпляр MS Word, а подключиться к уже запущенному экземпляру. В этом случае надо заменить вызов CreateOleObject() на GetActiveOleObject().
Здесь происходит попытка подключения к уже запущенному экземпляру MS Word. Затем, выполняется подключение к первому из уже открытых документов. Если все эти шаги успешны, то выполняется поиск текста. Ещё можно сделать так. Подключаемся к запущенному экземпляру MS Word. Потом, среди открытых документов ищем тот, чьё имя файла совпадает с заданным и выполняем поиск в этом документе. Имя файла пускай будет выбираться через OpenDialog.
1 |
Попросили меня доработать старый модуль, который генерирует клиентам компании письма в формате MS Word. Пользователи создают шаблоны, в которых расставляют названия полей, заключенные в служебные символы (например, #CONTRACT_NUM#, #FIO#, #ADDRESS#…). Программа по заданным критериям выбирает информацию о клиентах из базы и генерирует письма, находя в тексте шаблонов названия полей и заменяя их фактическими значениями.
Если опустить все детали и различную логику, то упрощенно это выглядит так:
Var
MSWord: OleVariant;
i: Integer;
q: TSDQuery;
…
begin
…
MSWord := CreateOleObject(‘Word.Application’);
MSWord.Documents.Add(‘какой то шаблон.dot’);
For i := 0 to q.FieldCount-1 do
MSWord.Selection.Find.Execute(FindText := ‘#’ + q.Fields[i].FieldName + ‘#’, ReplaceWith := q.Fields[i].AsString);
…
MSWord.ActiveDocument.SaveAs(‘письмо любимому клиенту.doc’);
…
Первое, с чем я столкнулся, это то, что длина значения параметра ReplaceWith не должна превышать 255 символов. Но это я обошел легко, заменив «поиск и замену текста» на «поиск и вывод текста»:
If MSWord.Selection.Find.Execute(FindText := ‘#’ + q.Fields[i].FieldName + ‘#’) then
MSWord.Selection.TypeText(q.Fields[i].AsString);
Вторая задачка оказалась сложнее. Внизу листа в фиксированное место необходимо было вывести фамилию и адрес получателя. Сначала я думал поместить его в нижний колонтитул, но оказалось, что письмо может быть на двух листах. Тогда ничего не оставалось, как использовать объект «заметка». Вставил «заметку». Красота! Документ генерируется, текст сдвигается, заметка остается на месте… Но радость была недолгой, т.к. поля #FIO# и #ADDRESS#, помещенные в заметку, так и остались незамененными
Оказалось, что MSWord.Selection.Find.Execute ищет текст только в основной части документа, а в документе, состоящем из разных структурных элементов (заметок, колонтитулов, сносок и т.д.), поиск необходимо производить отдельно в каждом из этих элементов. Все эти структурные элементы документа являются элементами коллекции StoryRanges. Т.к. дело было к ночи, а модуль должен был быть готов к утру, я не стал разбираться, как работать со StoryRanges через OLE из Delphi, и просто добавил в тестовый шаблон письма макрос на VBA, в котором перебираются все структурные элементы активного документа, в которых ведется поиск:
Sub ReplaceText(sFindText As String, sReplaceText As String)
Dim rngStory As Range
For Each rngStory In ActiveDocument.StoryRanges
With rngStory.Find
.Text = sFindText
.Replacement.Text = sReplaceText
.Wrap = wdFindContinue
.Execute Replace:=wdReplaceAll
End With
Next rngStory
End Sub
А в программе я только написал вызов макроса:
MSWord.Application.Run(‘ReplaceText’, ‘#FIO#’, ‘Иванов Иван Иванович’);
Работает как часы
P.S. После окончания генерации документа, если MS Word вам больше не нужен, то не забывайте закрывать его и высвобождать память
MSWord.Quit;
MSWord := UnAssigned;
P.P.S. Для подобной задачи генерации писем в формат MS Word больше подходит не поиск и замена текста, использование полей с переменными (DocVariable) и закладок (Bookmark). А как это сделать, я расскажу в следующий раз.
← →
Mefodiy
(2005-09-14 18:01)
[0]
В программе есть TEdit, в котором набирается слово на русском языке. На нажатие кнопки прописано:
WordApp: OleVariant;
WordApp := CreateOleObject(«Word.Application»);
WordApp.Documents.Open(«MyWordDocument»);
WordApp.Application.Keyboard(1049);
WordApp.Visible := True;
………………………………………………………………..
WordApp.Selection.Find.ClearFormatting;
WordApp.Selection.Find.Replacement.ClearFormatting;
WordApp.Selection.Find.Replacement.Text := «»;
WordApp.Selection.Find.Text := MyEdit.Text;
WordApp.Selection.Find.Forward := True;
WordApp.Selection.Find.Wrap := 1;
WordApp.Selection.Find.Format := False;
WordApp.Selection.Find.MatchCase := True;
WordApp.Selection.Find.MatchWholeWord := True;
WordApp.Selection.Find.MatchWildcards := False;
WordApp.Selection.Find.MatchSoundsLike := False;
WordApp.Selection.Find.MatchAllWordForms := False;
WordApp.Selection.Find.Execute;
Все работает без ошибок, но слово не находится, т.к. когда просматриваю через CTRL+F в Wordе, то видно, что русские буквы преобразовались в аброкадабру.
У меня Win XP. В региональных установках стоит русский язык.
В чем дело и главное что делать?
Спасибо
← →
Defunct ©
(2005-09-15 05:33)
[1]
может попробовать так:
WS : WideString;
WS := MyEdit.Text;
WordApp.Selection.Find.Text := WS;
?
← →
Mefodiy
(2005-09-15 08:05)
[2]
Уже пробовал. Не помогает.
← →
Mefodiy
(2005-09-16 16:27)
[3]
Мастера, отзовитесь !!!!!!!!!!!!!!!!!!!!!!
← →
lookin ©
(2005-09-16 16:32)
[4]
У меня Ваш код работает на ура…
← →
Mefodiy
(2005-09-16 17:01)
[5]
С английскими буквами и числами у меня тоже работает.
Проблема в кириллице. Насколько я понимаю в Дельфи используется ASCII кодировка, а в Word — Unicode. Просто я никак не могу в WordApp.Selection.Find.Text преобразовать и записать в кодировке Unicode. Не знаю возможно ли такое вообще. Использвание StringToWhideChar ничего не меняет.
← →
lookin ©
(2005-09-16 17:10)
[6]
[5] Mefodiy (16.09.05 17:01)
У меня с русским словом все ок…
← →
Mefodiy
(2005-09-16 17:16)
[7]
Может быть дело в версии Word? У меня — Word 2003.
← →
lookin ©
(2005-09-16 17:20)
[8]
[7] Mefodiy (16.09.05 17:16)
Может быть, у меня Ворд2000 и дельфи 5… И система Win2000.
← →
Mefodiy
(2005-09-17 10:03)
[9]
Обнаружил следующее:
Если объявить переменную
var s: WideString;
в теле программы присвоить ей какую нибудь строку кириллицы
s := «КИРИЛЛИЦА»;
и задать
WordApp.Selection.Find.Text := s;
то все нормально работает.
Но если задать
s := WordEdit.Text;
WordApp.Selection.Find.Text := s;
опять выходит аброкадабра.
Кстати вариант
s := WideString(WordEdit.Text);
тоже не помогает
← →
Mefodiy
(2005-09-17 11:21)
[10]
Проблема решиласть с помощью функции:
function StringToWideString(const s: AnsiString; codePage: Word): WideString;
var l: integer;
begin
if s = «» then
Result := «»
else
begin
l := MultiByteToWideChar(codePage, MB_PRECOMPOSED, PChar(@s[1]), -1, nil, 0);
SetLength(Result, l — 1);
if l > 1 then
MultiByteToWideChar(CodePage, MB_PRECOMPOSED, PChar(@s[1]),
-1, PWideChar(@Result[1]), l — 1);
end;
end;
которую откопал в интернете.
Вызывать ее надо так
WordApp.Selection.Find.Text := StringToWideString(WordEdit.Text,1251);
К сожалению через finddialog Вы не можете ограничить поиск только по одному столбцу, а потом Вам нужно получить значения остальных столбцов в строке, и как Вы собираетесь это сделать?:)
Если у Вас проблема с переносом данного решения в Делфи, то в честь Рождества помогу Вам примером:
Код:
В uses добавляем OleServer, COMObj
Код:
procedure TForm1.Button1Click(Sender: TObject);
var
Word, Excel: OleVariant; // Указатели на приложения
Path: OleVariant;
Text, Needle: string;
i, j, k, c, cc: integer;
begin
// Основные переменные
Path:=’C:1.doc’; // Путь к исходному документу
Needle:=’4′; // Текст для поиска в столбце
Word:=CreateOleObject(‘Word.Application’); // Создаем экземпляр Word’a
Word.Visible:=false; // Показываем или скрываем окно приложения
Excel:=CreateOleObject(‘Excel.Application’); // Создаем экземпляр Excel’a
Excel.Visible:=true; // Показываем или скрываем окно приложения
Excel.Workbooks.Add(EmptyParam); // Создаем пустую книгу Excel’a
c:=0; // Сбрасываем счетчик строк в Excel (можно использовать Вставку строк, но мы будем напрямую задавать значение)
// Открываем исходный документ
Word.Documents.Open(Path, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam);
// И далее по алгоритму, проверяем что таблицы есть в документе
if Word.ActiveDocument.Tables.Count >= 1 then begin
// ShowMessage(‘Найдено таблиц: ‘+IntToStr(Word.ActiveDocument.Tables.Count));
// Циклом проходим все таблицы
for i := 1 to Word.ActiveDocument.Tables.Count do begin
// Проходим строки текущей таблицы в первом столбце, ищем нужный текст
for j := 1 to Word.ActiveDocument.Tables.Item(i).Rows.Count do begin
// Выделяем полученную ячейку
Word.ActiveDocument.Tables.Item(i).Cell(j, 1).Select;
// Получаем значение ячейки
Text := Word.Selection.Text;
// Обрезаем последние 2 символа
Text := copy(Text,0,Length(Text)-2);
// ShowMessage(Text);
// Сравниваем значение первой колонки с необходимым
if Text = Needle then begin
Inc(c); // Переходим на следующую строку в Excel
cc:=1; // Сбрасываем счетчик столбцов в Excel
// Добавляем в Excel значение первого столбца
Excel.Range[Excel.Cells[c,cc],Excel.Cells[c,cc]].Select;
Excel.ActiveCell.FormulaR1C1 := Text;
// Теперь в цикле можно получить значения остальных колонок (первую пропускаем) в данной строке
for k := 2 to Word.ActiveDocument.Tables.Item(i).Columns.Count do begin
Inc(cc);
// Выделяем полученную ячейку из столбца k
Word.ActiveDocument.Tables.Item(i).Cell(j, k).Select;
// Получаем значение ячейки
Text := Word.Selection.Text;
// Обрезаем последние 2 символа
Text := copy(Text,0,Length(Text)-2);
// ShowMessage(Text);
// Теперь можно сохранить эти данные в отдельный коллектор либо сразу же вставлять в Excel
Excel.Range[Excel.Cells[c,cc],Excel.Cells[c,cc]].Select;
Excel.ActiveCell.FormulaR1C1 := Text;
end;
end;
end;
end;
end else begin
ShowMessage(‘В документе отсутствуют таблицы’);
end;
try
// Закрываем документ
Word.Quit;
//Excel.Quit;
except
end;
// Убиваем приложение
Word:=Unassigned;
//Excel:=Unassigned;
end;
Во вложении — весь проект + исходный вордовский документ
Успехов!
Форум программистов Vingrad
Модераторы: MetalFan |
Поиск: |
|
Поиск в Ворде фразы и вставка ее в Дельфи |
Опции темы |
Artem2005 |
|
||
Новичок Профиль Репутация: нет
|
Делаю программу по тестированию, и необходимо что-бы в дельфи вставлялся конкретный вопрос находящийся в doc файле |
||
|
|||
<Spawn> |
|
||
Око кары:) Профиль
Репутация: нет
|
Какой то странный подход. Почему не создать простенькую базу для хранения вопросов? ——————— «Для некоторых людей программирование является такой же внутренней потребностью, подобно тому, как коровы дают молоко, или писатели стремятся писать» — Николай Безруков. |
||
|
|||
Artem2005 |
|
||
Новичок Профиль Репутация: нет
|
Дапустим я набираю вопрос в Ворде, и что дальше? |
||
|
|||
Medved |
|
||
Эксперт Профиль
Репутация: нет
|
Действительно старнный подход. Логика приложения строиться в зависимости от того, в каком редакторе будут набираться вопросы. А вы мне вот скажите, как будет стоиться приложение, если вопросы будете набирать ну например в PhotoShop? ——————— http://extreme.sport-express.ru/ |
||
|
|||
Darksquall |
|
||||
Опытный Профиль
Репутация: 1
|
Хоть и странно, но отвечаю. Пишем Функцию поиска слова.
И не забываем добавить функцию в класс TForm1 Теперь подключаемся к Doc файлу через компоненты со вкладки Servers, WordApplications и WordDocument.
Это сообщение отредактировал(а) Darksquall — 27.1.2005, 12:41 ——————— www.bankcards.su |
||||
|
|||||
|
Правила форума «Delphi: ActiveX/СОМ/CORBA» | |
|
Запрещено: 1. Публиковать ссылки на вскрытые компоненты 2. Обсуждать взлом компонентов и делиться вскрытыми компонентами
Если Вам помогли, и атмосфера форума Вам понравилась, то заходите к нам чаще! С уважением, Rrader, Girder. |
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей) |
0 Пользователей: |
« Предыдущая тема | Delphi: ActiveX/СОМ/CORBA | Следующая тема » |
16.01.07 — 10:40
Текст ищется, но как мне потом узнать его позицию?
WordDocument1.Range.Find.ClearFormatting;
if WordDocument1.Range.Find.Execute(str, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam) then
begin
???
Хотелось бы чего-нибудь вроде:
RangeStart:=WordDocument1.Range.Find.Start;
RangeFin:=WordDocument1.Range.Find.End_;
1 — 16.01.07 — 11:07
Позицию в чем ? в доке/парагрофе/… ?
А так
fnd=WordDocument1.Range.Find;
fnd.ClearFormatting;
fnd.Execute(str, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam);
if fnd.Found=True then
begin
RangeStart:=fnd.Parent.Start;
RangeFin:=fnd.Parent.End;
///
Только вот зачем ???!
2 — 16.01.07 — 11:13
(1)
Зачем? Анализ документа по содержанию.
RangeStart:=fnd.Parent.Start;
RangeFin:=fnd.Parent.End;
Нет там таких параметров — Start, End.
3 — 16.01.07 — 11:16
(2) Кто тебе сказал что нету ?
Start();
End();
«Зачем? Анализ документа по содержанию» — а зачем позиция ? и позиция какая док/секция/контент/параграф и т.д. и т.п.
4 — 16.01.07 — 11:19
Найти позицию в документе.
Start() тоже нет. И End() нет. Пробовал.
Позиция для выдирания данных, следующих за найденной строкой.
Модератор
5 — 16.01.07 — 11:26
Попробуй запустить Запись макроса — тебе Ворд нарисует макрос, где все методы показаны. Я так делал.
6 — 16.01.07 — 11:26
(4) Да ну, вот тебе рабочий код на васике
Sub nnn()
Set fnd = ThisDocument.Range.Find
fnd.Execute ("eee")
If fnd.Found Then
MsgBox fnd.Parent.End()
MsgBox fnd.Parent.Start()
End If
End Sub
7 — 16.01.07 — 11:45
Пробовал запись макроса, там пишет просто Execute после установки параметров.
Ребята, мне на Делфях надо, через Ole…
А оттуда Start/End нет/не видит в ворде почему-то
8 — 16.01.07 — 11:48
(7) Смотри внимательно (6)
9 — 16.01.07 — 11:51
(8) Ну и? В делфях есть WordDocument1.Range.Find.Parent, но свойств Start, Start(), End, End() нет, т. е. WordDocument1.Range.Find.Parent.Start/WordDocument1.Range.Find.Parent.Start() не катит.
10 — 16.01.07 — 11:56
(9) А ты пробовал ?
11 — 16.01.07 — 11:56
Parent это Range, а у ранжа есть и старт и енд
12 — 16.01.07 — 11:58
(10) Ну блин, ну конечно пробовал. У меня Делфя открыта, и все, что советуете, пробую сразу же.
Действительно, WordDocument1.Range.Start есть, а WordDocument1.Range.Find.Parent.Start нет почему-то. У меня вот из-за этого-то и возник вопрос для открытия темы.
13 — 16.01.07 — 12:01
Нету дельфи
Вот джабаскр
word=new ActiveXObject(«Word.Application»);
doc=word.Documents.Open(«c:/find.doc»);
fnd = doc.Range().Find;
fnd.Execute («eee»);
if (fnd.Found==true){
WScript.Echo(fnd.Parent.End);
WScript.Echo(fnd.Parent.Start);
}
doc.close();
word.quit();
14 — 16.01.07 — 12:05
(13)
WScript.Echo(fnd.Parent.End);
WScript.Echo(fnd.Parent.Start);
А это что за извращение еще?
15 — 16.01.07 — 12:06
Нет такого в делфях — WScript
16 — 16.01.07 — 12:10
(15) Дак это понятно, это просто вывожу на экран
понятно что там messagebox
17 — 16.01.07 — 12:12
Тогда я чего-то не понимаю… В чем смысл (13)?
Свой кусок кода я привел в (0)
18 — 16.01.07 — 12:23
И все-таки, что за глюк — (12)?
19 — 16.01.07 — 13:20
Сделай попробуй переменные типа объект (ну как там в дельфи правильно, хотя вроде в паскале нет приведения типов, но х.з.)
fnd:=WordDocument1.Range().Find;
и
Rang=fnd.Parent;
20 — 16.01.07 — 15:09
(19) Так съел Start, но значение всегда 0.
21 — 16.01.07 — 15:27
(19) Слушай, вроде как заработало.
fnd.Parent.Select;
rang:=fnd.Parent;
d1:=rang.Start;
d2:=rang.End;
Chubrik
22 — 16.01.07 — 15:27
Большое спасибо за ответы!