Posted: Mon Oct 08, 2007 9:31 pm Post subject: Создание элементов ActiveX в Visual C++ 5
С. Холзнер. Microsoft Visual C++ 5 с самого начала Создание элементов ActiveX
В этом уроке мы научимся работать с элементами ActiveX - компонентами, которые можно внедрять в программы, а также в Web-страницы, отображаемые с помощью броузера. Научившись создавать элементы ActiveX, мы сможем включать их в другие программы, что открывает множество полезных возможностей. Более того, их даже можно включить в палитру редактора диалоговых окон и непосредственно перетаскивать в создаваемое диалоговое окно.
Мы научимся создавать элементы ActiveX и пользоваться ими как в специальной программе- <тестовом контейнере>, поставляемой вместе с VisualC++, так и в других программах, написанных на VisualC++. Мы увидим, как организовать прорисовку элемента ActiveX, чтобы он выглядел так, как нам требуется.
Кроме того, мы научимся порождать элементы ActiveX от стандартных управляющих элементов(например, кнопок). Если по своему набору функций ваш элемент похож на один из стандартных элементов, такая методика заметно облегчит вашу работу.
Наконец, мы узнаем, как внедрить в элементы ActiveX методы, которые могут вызываться из других программ, и свойства (хранимые в виде внутренних переменных), значения которых могут задаваться другими программами. Элементы ActiveX могут поддерживать свои, нестандартные события, они также будут упомянуты в этом уроке.
Приступим к нашему первому элементу ActiveX - boxer.
Элемент ActiveX Boxer
Наш первый элемент ActiveX рисует на экране небольшой прямоугольник, разделенный на четыре части; когда пользователь щелкает мышью в одной из них, та окрашивается в черный цвет:
Если пользователь щелкнет другую часть, закраска переходит на нее:
Запустите VisualC++ и начните создание программы boxer. На этот раз в диалоговом окне New выберите строку MFC ActiveX ControlWizard (см.рис.13.1).
Рис.13.1. Создание элемента ActiveX
Создание управляющего элемента при помощи MFC ActiveX ControlWizard занимает всего два этапа. Подтвердите все установки по умолчанию кнопкой Finish; ControlWizard завершает создание элемента.
Код элемента в созданном проекте содержится в файлах BoxerCtl.h и BoxerVtl.cpp. Он является производным от класса COleControl, а содержимое файла BoxerCtl.cpp отчасти напоминает код стандартного класса вида. Например, в нем присутствует метод OnDraw(), в котором происходит рисование элемента.
Рисование элемента ActiveX
В настоящий момент метод OnDraw() в файле BoxerCtl.cpp выглядит так:
Методу OnDraw() передается прямоугольник rcBounds, в пределах которого должен быть нарисован элемент, а заранее помещенный в него код закрашивает этот прямоугольник белым цветом и рисует в нем эллипс. Мы нарисуем в элементе собственное изображение, поэтому удалите из OnDraw() строку для рисования эллипса: pdc->Ellipse(rcBounds);.
Мы разделим область элемента на четыре прямоугольника с именами box1-box4 и объявим их в файле BoxerCtl.h:
В методе OnDraw() происходит разделение области элемента на четыре части и размещение прямоугольников в левом верхнем, правом верхнем, левом нижнем и правом нижнем углу:
Прямоугольники появились на экране. Следующим шагом должна стать обработка сообщений от мыши и закраска прямоугольника, на котором щелкнул пользователь.
Создание обработчика сообщения в элементе ActiveX
Элементы ActiveX поддерживают обработку сообщения точно так же, как и остальные программы- воспользуйтесь ClassWizard и создайте в нашем элементе обработчик для сообщения WM_LBUTTONDOWN (см. рис.13.2).
Рис.13.2. Добавление поддержки работы с мышью для элемента ActiveX
Метод OnLButtonDown() в коде для элемента ActiveX выглядит так:
Code:
void CBoxerCtrl::OnLButtonDown(UINT nFlags, CPoint point)
{ // TODO: добавьте код обработки сообщения
// и/или вызовите обработчик по умолчанию
COleControl::OnLButtonDown(nFlags, point);
}
Мы должны определить, в каком из четырех прямоугольников щелкнул пользователь, и закрасить этот прямоугольник- для этого в заголовочном файле CBoxerCtl.h объявляются четыре новые переменные (они будут выполнять функции флагов):
Code:
class CBoxerCtrl : public COleControl
{
.
.
.
// Реализация
В методе OnLButtonDown() можно установить значения флагов с помощью удобного метода PtInRect() класса CRect. Он возвращает true, если точка-параметр принадлежит прямоугольнику (в нашем случае прямоугольникам box1-box4):
После установки флага для прямоугольника, на котором щелкнул пользователь, мы вызываем перерисовку вида методом Invalidate(). Это приводит к вызову OnDraw(), в котором проверяются все четыре флага и необходимый прямоугольник закрашивается методом FillSolidRect() класса CDC:
Программа готова, можно приступать к тестированию.
Тестирование элемента ActiveX
Чтобы проверить работу элемента Boxer, выполните команду Build|Build Boxer.ocx (управляющие элементы ActiveX получают расширение .ocx) - элемент создается и регистрируется в Windows. Затем выполните команду Tools|ActiveX Control Test Container. Запускается чрезвычайно полезная программа <тестовый контейнер> (см.рис.13.3).
Выполните в тестовом контейнере команду Edit|Insert OLE Control и дважды щелкните на строке Boxer control в окне Insert OLE Control. В результате наш элемент ActiveX появится в контейнере (см. рис.13.3).
Щелкните в любом прямоугольнике - он закрашивается черным цветом, как показано на рис.13.3. Если щелкнуть в другом прямоугольнике, черным станет он. Элемент работает так, как мы хотели, а сейчас мы попытаемся вставить его в программу.
Рис.13.3. Тестирование элемента ActiveX
Использование элемента ActiveX в программе на VisualC++
С помощью AppWizard создайте программу на базе диалогового окна и назовите ее Boxerapp. В нее можно вставить элемент типа Boxer - выполните команду Project|Add To Project|Components and Controls, и откроется окно Components and Controls Gallery. Дважды щелкните на строке Registered ActiveX Controls, чтобы вывести список всех доступных элементов ActiveX (см.рис.13.4).
Рис.13.4. Окно Components and Controls Gallery
Выделите строку Boxer Control (см. рис.13.4) и нажмите кнопку Insert. Появляется диалоговое окно с вопросом, желаете ли вы вставить элемент в свою программу; нажмите кнопку OK. Затем откроется окно Confirm Class, сообщающее о создании нового класса CBoxer. Снова нажмите кнопку OK и закройте окно Components and Controls Gallery. Элемент Boxer появится в палитре редактора диалоговых окон (см.рис. 13.5). Он представлен кнопкой с буквами OCX.
Чтобы изменить внешний вид элемента ActiveX в палитре, дважды щелкните в VisualC++ на идентификаторе растрового ресурса IDB_BOXER - значок откроется в редакторе растровых изображений, и вы сможете отредактировать его по своему усмотрению.
Рис.13.5. Элемент ActiveX в палитре
Элемент Boxer, как и любой другой управляющий элемент, можно разместить в главном диалоговом окне программы (см. рис. 13.5). Запустите программу (см. рис.13.6).
Рис.13.6. Элемент ActiveX в программе
Как видите, элемент ActiveX присутствует в программе и работает так, как ему положено. Вы можете как угодно щелкать на прямоугольниках, и каждый раз выбранный вами прямоугольник будет закрашиваться (см. рис.13.6). Итак, нам удалось создать свой первый элемент ActiveX!
Исходный текст программы содержится в файлах BoxerCtl.h/BoxerCtl.cpp.
// Диспетчерские схемы
//{{AFX_DISPATCH(CBoxerCtrl)
// ВНИМАНИЕ - здесь ClassWizard вставляет
// и удаляет функции.
// НЕ ИЗМЕНЯЙТЕ содержимое этих фрагментов
// сгенерированного кода!
//}}AFX_DISPATCH
DECLARE_DISPATCH_MAP()
afx_msg void AboutBox();
// Схемы событий
//{{AFX_EVENT(CBoxerCtrl)
// ВНИМАНИЕ - здесь ClassWizard вставляет
// и удаляет функции.
// НЕ ИЗМЕНЯЙТЕ содержимое этих фрагментов
// сгенерированного кода!
//}}AFX_EVENT
DECLARE_EVENT_MAP()
// Диспетчерские идентификаторы и идентификаторы событий
public:
enum
{
//{{AFX_DISP_ID(CBoxerCtrl)
// ВНИМАНИЕ - здесь ClassWizard вставляет
// и удаляет элементы перечисляемого типа.
// НЕ ИЗМЕНЯЙТЕ содержимое этих фрагментов
// сгенерированного кода!
//}}AFX_DISP_ID
};
};
//{{AFX_INSERT_LOCATION}}
// Microsoft Developer Studio вставляет дополнительные
// объявления перед предшествующей строкой.
BEGIN_DISPATCH_MAP(CBoxerCtrl, COleControl)
//{{AFX_DISPATCH_MAP(CBoxerCtrl)
// ВНИМАНИЕ - здесь ClassWizard вставляет
// и удаляет элементы схемы.
// НЕ ИЗМЕНЯЙТЕ содержимое этих фрагментов
// сгенерированного кода!
//}}AFX_DISPATCH_MAP
DISP_FUNCTION_ID(CBoxerCtrl, "AboutBox",
DISPID_ABOUTBOX, AboutBox, VT_EMPTY, VTS_NONE)
END_DISPATCH_MAP()
///////////////////////////////////////////////////////////
// Схема событий
BEGIN_EVENT_MAP(CBoxerCtrl, COleControl)
//{{AFX_EVENT_MAP(CBoxerCtrl)
// ВНИМАНИЕ - здесь ClassWizard вставляет
// и удаляет элементы схемы.
// НЕ ИЗМЕНЯЙТЕ содержимое этих фрагментов
// сгенерированного кода!
//}}AFX_EVENT_MAP
END_EVENT_MAP()
// TODO: При необходимости добавьте новые
// страницы свойств. // Не забудьте увеличить значение счетчика!
BEGIN_PROPPAGEIDS(CBoxerCtrl, 1)
PROPPAGEID(CBoxerPropPage::guid)
END_PROPPAGEIDS(CBoxerCtrl)
///////////////////////////////////////////////////////////
// Инициализация фабрики класса и guid
///////////////////////////////////////////////////////////
// CBoxerCtrl::CBoxerCtrlFactory::UpdateRegistry -
// добавляет или удаляет записи системного реестра
// для CBoxerCtrl
BOOL CBoxerCtrl::CBoxerCtrlFactory::UpdateRegistry
(BOOL bRegister)
{
// Убедитесь, что ваш элемент соответствует требованиям
// совместной потоковой модели. Подробности приведены
// в документе MFC TechNote 64.
// Если элемент нарушает требования совместной модели,
// необходимо модифицировать следующий фрагмент программы
// и заменить 6-й параметр с afxRegApartmentThreading
// на 0.
// TODO: Вызовите функции PX_ для каждого
// устойчивого свойства.
}
///////////////////////////////////////////////////////////
// CBoxerCtrl::OnResetState - сброс элемента в состояние
// по умолчанию
void CBoxerCtrl::OnResetState()
{
COleControl::OnResetState(); // Присваивает значения по
// умолчанию из DoPropExchange
// Сбросьте любые другие параметры состояния элемента
}
void CBoxerCtrl::OnLButtonDown(UINT nFlags, CPoint point)
{ // TODO: добавьте код обработки сообщения
// и/или вызовите обработчик по умолчанию
fill1 = box1.PtInRect(point);
fill2 = box2.PtInRect(point);
fill3 = box3.PtInRect(point);
fill4 = box4.PtInRect(point);
Invalidate();
COleControl::OnLButtonDown(nFlags, point);
}
Мы посмотрели, как создать элемент ActiveX и организовать его прорисовку. Однако на этом возможности ActiveX отнюдь не исчерпываются - управляющие элементы могут также обладать событиями, методами и свойствами. Сейчас мы познакомимся с этой темой подробнее.
Создание элемента ActiveX на базе кнопки
Следующий пример демонстрирует ряд новых аспектов программирования ActiveX- например, возможность создания элементов на базе уже существующих управляющих элементов (например, кнопок). Мы также увидим, как организовать в элементах ActiveX поддержку событий- программа, в которую внедрен такой элемент, сможет обработать событие так же, как она обрабатывает любоесообщение Windows. В нашем случае элемент будет называться Buttoner и поддерживать событие Click. Присутствующий в программе метод-обработчик (скажем, OnClickButtonerctrl1()) будет вызываться при щелчке мышью внутри элемента.
Кроме того, в новом примере будут поддерживаться методы элементов ActiveX- в частности Beep(). В других программах вы сможете при помощи ClassWizard создавать переменные, связанные с нашим элементом (например, m_buttoner), и вызывать метод с помощью записи типа m_buttoner.Beep().
Наконец, мы рассмотрим свойства элементов ActiveX. В нашем примере будет поддерживаться свойство с именем data. В нем будет храниться количество щелчков мышью, сделанных на элементе. Существуют два способа обращения к свойству data из других программ: можно либо разрешить ссылаться на свойство в записи типа m_buttoner.data и непосредственно читать и присваивать значение этого свойства, либо воспользоваться более надежным способом и предоставить специальные методы для обращения к свойству. В этом случае другая программа должна вызывать метод m_buttoner.SetData(), чтобы присвоить значение свойству, и m_buttoner.GetData(), чтобы получить его. Использование методов для доступа более надежно, поскольку присваиваемое значение можно предварительно проверить. В нашем примере будет использоваться именно этот вариант.
Создайте программу Buttoner с помощью ControlWizard. Во втором окне ControlWizard выберите строку BUTTON из раскрывающегося списка под вопросом Which window class, if any, should this control subclass? (Какой оконный класс, если таковой существует, должен субклассировать данный элемент?), как показано на рис.13.7, и завершите создание элемента кнопкой Finish.
Рис.13.7. Создание элемента Buttoner на базе кнопки
Можно приступать к настройке элемента Buttoner.
Настройка элемента Buttoner
Пока что наш новый элемент ActiveX выглядит, как пустая кнопка, однако мы можем настроить его внешний вид и поместить на него надпись- например, <Нажми меня!>. Мы сделаем это в конструкторе элемента, расположенном в файле ButtonerCtl.cpp:
Элемент Buttoner, как и все управляющие элементы ActiveX, порождается от класса COleControl, поэтому мы можем воспользоваться методом SetText() этого класса и изменить надпись на элементе:
Можно приступать к программированию. Начнем с добавления событий в элемент ActiveX. Другие программы, использующие его, могут содержать обработчики, вызываемые при возникновении события.
Рис.13.8. Настройка элемента ActiveX
Добавление событий
Мы создадим в элементе Buttoner событие Click, происходящее при щелчке мышью. Возникновение события (с извещением программы, в которую внедрен элемент ActiveX) называется возбуждением (firing) события, поэтому говорят, что при щелчке мышью программа возбуждает событие Click. При возбуждении события Click вызывается его обработчик в программе, содержащей наш элемент. Давайте посмотрим, как это делается.
В ClassWizard перейдите на вкладку ActiveX Events и нажмите кнопку Add Event. Открывается одноименное окно (см. рис.<N>13.9).
Рис.13.9. Добавление события к элементу ActiveX
В нем мы и создадим событие Click. Выберите строку Click в раскрывающемся списке External name (см.рис.13.9) - в наш элемент добавляется событие Click. Чтобы событие действительно произошло, нужно вызвать метод FireClick(), который также указан в окне Add Event.
Если вы хотите, чтобы новое событие вызывалось с параметрами (которые будут передаваться обработчику программы, работающей с элементом), введите параметры и их типы в списке Parameter, расположенном в нижней части окна Add Event.
Событие Click будет возбуждаться при щелчке левой кнопкой мыши, поэтому сейчас мы включим в элемент ActiveX обработчик OnLButtonDown():
Code:
void CButtonerCtrl::OnLButtonDown(UINT nFlags, CPoint point)
{
// TODO: добавьте код обработки сообщения
// и/или вызовите обработчик по умолчанию
COleControl::OnLButtonDown(nFlags, point);
}
Чтобы возбудить событие Click, достаточно вызвать специальный метод FireClick(), созданный ClassWizard (если событие передает обработчикам какие-либо параметры, их следует передать методу FireEvent(), где Event - имя вашего события).
Эта строка приводит к тому, что при каждом щелчке мышью будет возбуждаться событие Click, а программы, работающие с элементом, смогут отреагировать на данное событие в обработчике.
Мы научились возбуждать события из элемента ActiveX. Число щелчков должно сохраняться в свойстве data, и сейчас мы создадим это свойство.
Добавление свойств к элементам ActiveX
Свойством называется переменная класса элемента. Если тот содержит свойство с именем data, то другие программы могут обращаться к нему через запись типа m_buttoner.data или через функции чтения/записи свойства: m_buttoner.SetData() и m_buttoner.GetData(). Мы познакомимся со свойствами на примере свойства data, сохраняющего число щелчков мышью.
Для добавления свойств в элемент ActiveX также используется ClassWizard. Откройте его и перейдите на вкладку Automation (ранее называлась OLE Automation), затем нажмите кнопку Add Property, чтобы вызвать одноименное окно (см.рис.13.10).
Введите в поле External name имя свойства data; сделайте его коротким целым числом, выбрав из списка Type строку short. Установите переключатель Get/Set methods- это значит, что программы будут вызывать метод SetData(), чтобы присвоить значение свойству, и метод GetData(), чтобы узнать значение свойства (если установить переключатель Member Variable, другие программы смогут напрямую обращаться к свойству: m_buttoner.data). Нажмите кнопку OK, чтобы закрыть окно Add Property, а потом закройте ClassWizard.
Рис.13.10. Окно Add Property
Теперь в программе можно ссылаться на значение свойства data через переменную m_data, начнем с обнуления этого значения в конструкторе:
Если другая программа вызовет метод GetData() нашего элемента, она получит значение переменной m_data, равное количеству щелчков, сделанных на нем.
Добавление свойства закончено. К настоящему моменту наш элемент имеет событие Click и свойство data. Сейчас мы добавим в него метод Beep(), которыйможет вызываться другими программами с помощью записи вида m_buttoner.Beep().
Добавление методов
Чтобы добавить в элемент новый метод, вызовите ClassWizard и перейдите на вкладку Automation. Нажмите кнопку Add Method, и откроется одноименное окно (см.рис.13.11).
Рис.13.11. Окно Add Method
Присвойте новому методу имя Beep (в обоих полях - External name и Internal name) и укажите тип возвращаемого значения void (см.рис. 13.11). Нажмите кнопку OK, чтобы закрыть окно Add Method, а потом закройте ClassWizard. В элементе появляется метод Beep():
Code:
void CButtonerCtrl::Beep()
{
// TODO: добавьте код диспетчерской обработки
}
При вызове этого метода компьютер будет выдавать звуковой сигнал, для этого мы воспользуемся методом MFC MessageBeep():
В нашем элементе появился новый метод, который может использоваться в других программах. Теперь они могут вызывать метод Beep() элемента внутри своего кода.
Настало время посмотреть, как элемент Buttoner ведет себя в других приложениях. Давайте попробуем внедрить его в программу и поработать с ним на уровне программного кода.
Внедрение элемента Buttoner в другую программу
При помощи AppWizard создайте новую программу на базе диалогового окна и присвойте ей имя Buttonerapp. Включите элемент Buttoner в палитру редактора диалоговых окон. Для этого следует вызвать окно Components and Controls Gallery, о чем рассказывалось в предыдущем примере. Когда VisualC++ предложит задать имя класса элемента, подтвердите предложенное по умолчанию имя CButtoner.
Теперь мы добавим элемент Buttoner в новую программу с помощью редакторадиалоговых окон (см. рис.13.12). Он присваивает элементу идентификатор ID_BUTTONERCTRL1.
Рис.13.12. Элемент Buttoner в новой программе
Рис.13.13. Создание переменной для элемента ActiveX
Разместите над элементом Buttoner надпись Щелкните элемент Buttoner:, а под ним- текстовое поле для вывода сведений о количестве щелчков. Содержимое поля будет определяться методом GetData(), возвращающим значение свойства data.
С помощью ClassWizard свяжите с элементом Buttoner переменную m_buttoner (см. рис.13.13). Обратите внимание: она принадлежит недавно созданному классу CButtoner. Кроме того, свяжите содержимое текстового поля с переменной m_text (см. рис.13.13).
Элемент Buttoner внедрен в приложение Buttonerapp, и для вызова его методов (например, Beep()) можно пользоваться записью вида m_buttoner.Beep().
Связывание элемента ActiveX с кодом программы
В ClassWizard мы создадим обработчик для события Click элемента Buttoner. Найдите событие в окне ClassWizard (см.рис.13.14) и создайте новый обработчик, которому ClassWizard присваивает имя OnClickButtonerctrl1():
Code:
void CButtonerappDlg::OnClickButtonerctrl1()
{
// TODO: добавьте код обработки события
// и/или вызовите обработчик по умолчанию
}
Рис.13.14. Создание обработчика для события от элемента ActiveX
Этот метод будет вызываться при каждом щелчке мышью на элементе. Чтобы узнать, сколько раз пользователь щелкнул на элементе, следует определить значение свойства data, а для этого нужно вызвать метод GetData():
Code:
void CButtonerappDlg::OnClickButtonerctrl1()
{
m_text.Format("свойство data = %ld", m_buttoner.GetData());
UpdateData(false);
}
Теперь при каждом щелчке на элементе программа Buttonerapp будет выводить общее количество щелчков, хранящееся в свойстве data (см.рис. 13.15). Наша программа, содержащая управляющий элемент ActiveX, работает именно так, как нам хотелось.
На примере элемента Buttoner мы узнали много нового- как создать элемент ActiveX на базе существующего управляющего элемента и как организовать в нем поддержку событий, методов и свойств.
Исходный текст элемента Buttoner содержится в файлах ButtonerCtl.h/ButtonerCtl.cpp, а исходный текст программы Buttonerapp- в файлах ButtonerappDlg.h/ ButtonerappDlg.cpp.
Рис.13.15. Использование свойств и методов элемента ActiveX
DECLARE_OLECREATE_EX(CBoxerCtrl) // Фабрика класса и guid
DECLARE_OLETYPELIB(CBoxerCtrl) // GetTypeInfo
DECLARE_PROPPAGEIDS(CBoxerCtrl)// Идентификаторы
// страниц свойств
DECLARE_OLECTLTYPE(CBoxerCtrl) // Имя типа и
// вспомогательная
// информация
// Схемы событий
//{{AFX_EVENT(CButtonerCtrl)
//}}AFX_EVENT
DECLARE_EVENT_MAP()
// Диспетчерские идентификаторы и идентификаторы событий
public:
enum
{
//{{AFX_DISP_ID(CButtonerCtrl)
dispidData = 1L,
dispidBeep = 2L,
//}}AFX_DISP_ID
};
};
//{{AFX_INSERT_LOCATION}}
// Microsoft Developer Studio вставляет дополнительные
// объявления перед предшествующей строкой. #endif
// !defined(AFX_BUTTONERCTL_H__6AE5A16F_9584_11D0
// _8860_444553540000__INCLUDED)
// ButtonerCtl.cpp : реализация класса элемента
// ActiveX CButtonerCtrl.
// TODO: При необходимости добавьте новые
// страницы свойств.
// Не забудьте увеличить значение счетчика!
BEGIN_PROPPAGEIDS(CButtonerCtrl, 1)
PROPPAGEID(CButtonerPropPage::guid)
END_PROPPAGEIDS(CButtonerCtrl)
///////////////////////////////////////////////////////////
// Инициализация фабрики класса и guid
///////////////////////////////////////////////////////////
// CButtonerCtrl::CButtonerCtrlFactory::UpdateRegistry -
// добавляет или удаляет записи системного реестра
// для CBoxerCtrl
BOOL CButtonerCtrl::CButtonerCtrlFactory::
UpdateRegistry(BOOL bRegister) {
// Убедитесь, что ваш элемент соответствует требованиям
// совместной потоковой модели. Подробности приведены
// в документе MFC TechNote 64.
// Если элемент нарушает требования совместной модели,
// необходимо модифицировать следующий фрагмент программы
// и заменить 6-й параметр с afxRegApartmentThreading
// на 0.
// TODO: Вызовите функции PX_ для каждого устойчивого
// свойства.
}
///////////////////////////////////////////////////////////
// CButtonerCtrl::OnResetState - сброс элемента
// в состояние по умолчанию
void CButtonerCtrl::OnResetState()
{
COleControl::OnResetState(); // Присваивает значения
// по умолчанию из DoPropExchange
// Сбросьте любые другие параметры состояния элемента
}
///////////////////////////////////////////////////////////
// CButtonerCtrl::AboutBox - отображение диалогового
// окна About
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum You cannot attach files in this forum You can download files in this forum
All product names are trademarks of their respective companies. SAPNET.RU websites are in no way affiliated with SAP AG. SAP, SAP R/3, R/3 software, mySAP, ABAP, BAPI, xApps, SAP NetWeaver and any other are registered trademarks of SAP AG. Every effort is made to ensure content integrity. Use information on this site at your own risk.