Re[13]: Формат конфигов
От: VladD2 Российская Империя www.nemerle.org
Дата: 23.06.05 23:58
Оценка:
Здравствуйте, Павел Кузнецов, Вы писали:

>> Развей свою мысль и объясни чем будет проще править самопальный конфиг сделанный под впечателением от Кюрла и т.п.


ПК>В данном случае речь идет о правке конфигов такого формата:

ПК>
ПК>name1 = value1
ПК>name2 = value2
ПК>


Да? Ну, ты мастер подменять тему. А вот такой не хочешь:
{str_data_class    "smsg_2::smpp::a_smpp_transmitter_t::connection_state_monitor"
    {priority 5}
    {font_size 10}
    {if    {== "st_not_connected"}
        {pixmap    "etc/gemont_1/img/xpm/red_cross_24x16.xpm" }
        {bkcolor    "magenta"}
    }
    {if {== "st_connected"}
        {bkcolor    "blue"}
    }
    {if {== "st_bind"}
        {pixmap "etc/gemont_1/img/xpm/conn_exists_24x16.xpm" }
        {bkcolor    "green"}
    }
    {if {== "st_shutdown"}
        {bkcolor    "yellow"}
    }
}

{str_data_class    "smsg_2::smpp_smsc::a_smsc_t::m_connection_monitor_data"
    {priority 9}
    {font_size 11}
    {if    {== "st_not_bind"}
        {pixmap    "etc/gemont_1/img/xpm/red_cross_24x16.xpm" }
        {bkcolor    "magenta"}
        {sound    "etc/gemont_1/snd/wav/siren.wav"}
        {log_event {level "error"}
            {message "Нет соединения с SMSC"}}
    }
    {if {== "st_bind"}
        {pixmap "etc/gemont_1/img/xpm/conn_exists_24x16.xpm" }
        {bkcolor    "green"}
        {sound    "etc/gemont_1/snd/wav/online.wav"}
        {log_event {level "info"}
            {message "Есть соединение с SMSC"}}
    }
    {if {== "st_shutdown"}
        {bkcolor    "yellow"}
        {sound    "etc/gemont_1/snd/wav/tada.wav"}
    }
}


Что до инифайлов, то они конечно хороши... до тех пор пока информация умещается в рамках идиомы словаря. А как только данные в конфиге становятся иерархическими (или вообще грфом), то инифайлы становятся сущим адом. И намного проще будет обучить пользователя правть ХМЛ, чем изобретать свои под-форматы и обучать им пользователей. Ну, а еще разумнее будет сделать визуальный редактор. Мы вот вместо того чтобы трепаться в том же Янусе так и поступили. А ты можешь дальще искать гипотетические проблемы в ХМЛ. Мне эта тему уже надоела.
... << RSDN@Home 1.1.4 beta 7 rev. 466>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[11]: Формат конфигов
От: VladD2 Российская Империя www.nemerle.org
Дата: 23.06.05 23:58
Оценка:
Здравствуйте, Павел Кузнецов, Вы писали:

ПК> Нужно анализировать use cases своих пользователей, и на основе анализа принимать соответствующие решения. Пытаться следовать какой-либо заранее выбранной схеме без учета use cases своих пользователей (например, безусловно использовать XML, или безусловно "изобредать собственные языки на базе синтаксиса разных Кюрлов"), тем более навязывать другим свою схему без учета их use cases — зачастую ошибочно.


Особенно понравилась последняя фраза. Вспоминается нетленка — и ты права жена (с).
... << RSDN@Home 1.1.4 beta 7 rev. 466>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[11]: Формат конфигов
От: VladD2 Российская Империя www.nemerle.org
Дата: 23.06.05 23:58
Оценка:
Здравствуйте, eao197, Вы писали:

E> Им, видимо, делать больше нечего было.


Японцы. Что с них возьмшь?

E>Кстати, я не изобретал синтаксис -- он уже был изобретен, причем даже не авторами Курла. Я просто сделал парсер этого формата (уже четыре года тому) и использую его для работы с конфигами и подобными вещами. Более того, когда в 2001 году я узнал про Курл, я написал одному из разработчиков Курла о том, что для конфигов Курл удобнее XML (на примере конфига для Java Web Application). И со мной согласились.


Ну, вот теперь попробуй обдуать (я уж и не говорю сделать) что нужно добавить к твоему велосипеду, чтобы сделать универсальный ГУИ для его редактирования. Потом откой Янус... откой его настройки и погляди как редактируется ХМЛ в нем. Потом открой исходники януса и погляди как этот "ХМЛ" вглядит в коде. Думаю ты будешь удивлен, что это просто объект. Потом погляди на объем кода и попробуй оценить перерасход своих сил и потерю от не использования готовых решений и стандартов.
... << RSDN@Home 1.1.4 beta 7 rev. 466>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[13]: Формат конфигов
От: VladD2 Российская Империя www.nemerle.org
Дата: 23.06.05 23:58
Оценка:
Здравствуйте, eao197, Вы писали:

E>Ты бы еще сказал, что Windows == MFC

E>KDE просто использует Qt.

KDE написано на Qt, А МФЦ одна из оболочек к ВыньАПИ. Причем не самая удачная.

E>Да уж, в Apple явно дураки сидят


Да, уж до Били им как до пикина раком.
... << RSDN@Home 1.1.4 beta 7 rev. 466>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[13]: Формат конфигов
От: VladD2 Российская Империя www.nemerle.org
Дата: 23.06.05 23:58
Оценка:
Здравствуйте, Павел Кузнецов, Вы писали:

ПК>Internet Explorer HTML (т.е. стандартный HTML) показывает хуже, чем Mozilla или Opera. Лучше он показывает только сайты, разработанные специально для него. Т.е. в качестве встраиваемого компонента выбор IE можно объяснять скорее удобством его встраивания при использовании определенных API, нежели тем, как он отображает HTML.


Ой, не нужно мне это расскззывать. Расскажи это, ну, тому же рсдн-у. А то даже через файр-фокс смотреть на него не очень.
... << RSDN@Home 1.1.4 beta 7 rev. 466>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[14]: Формат конфигов
От: WFrag США  
Дата: 24.06.05 01:20
Оценка: +1 :)
Здравствуйте, VladD2, Вы писали:

VD>Ой, не нужно мне это расскззывать. Расскажи это, ну, тому же рсдн-у. А то даже через файр-фокс смотреть на него не очень.


http://validator.w3.org/check?uri=http%3A%2F%2Fgzip.rsdn.ru%2Ftoc%2F%3Furl%3DFrame%2FMain.aspx

http://validator.w3.org/check?uri=http%3A%2F%2Fgzip.rsdn.ru
... << RSDN@Home 1.1.4 beta 6a rev. 438>>
Re[34]: Формат конфигов
От: Павел Кузнецов  
Дата: 24.06.05 01:41
Оценка: 1 (1) +2 -1
Здравствуйте, VladD2, Вы писали:

>>> Напомню, что в замен предлагается написать парсер собственного формата на С++.


ПК>>Необязательно. Можно использовать любой из миллиона уже существующих. Например, для формата .ini-файлов.


VD>Ненужно подменять тему разговора. О приемуществе инифайлов над хмл я и говорить бы не стал. Если они удовлетворяют, то используй. Тоже какой-ни-какой но стандарт. Да и парсинг этого формата примитивне. Речь же шла о навороте очередных велосипедов на ровном месте. Поднимись на несколько сообщения вверх и перечитай их.


Влад, это два разных use case, не нужно валить их в одну кучу. К собственному формату, предложенному eao197, данные use cases не относятся вообще. Это были use cases не "за формат eao197", а против утверждений Андрея о безусловном преимуществе использования XML для конфигов.

>>> Можно привести обоснования разумности данного решения, а не пытаться заговорить зубы?


ПК>>Уже приводили use cases неоднократно: 1) нужно, чтоб пользователю было легко править конфигурационный файл "вручную";


VD>Значит нельзя? Жаль. А я бы хотел послушать про простоту правки вот этого:


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

VD>
VD>{str_data_class    "smsg_2::smpp::a_smpp_transmitter_t::connection_state_monitor"
VD>    {priority 5}
VD>    {font_size 10}
VD>    {if    {== "st_not_connected"}
VD>        {pixmap    "etc/gemont_1/img/xpm/red_cross_24x16.xpm" }
VD>        {bkcolor    "magenta"}
VD>    }
VD>

VD>По сравлению с форматом основанным на ХМЛ-е.

В данном случае XML отдыхает по полной программе по причине громоздкости и слабой читабельности. Точно также, например, как выбор XML для скриптов NAnt является откровенно неудачной идеей: выражение императивных конструкций через декларативные представляет собой достаточно жалкое зрелище.

ПК>> 2) нужно, чтоб можно было легко работать с форматом конфигурационного файла из компонент, в которых нет готовой поддержки XML, но есть, например, поддержка регулярных выражений.


VD>И как ты это собирашся обесечить для подобного формата?


Это все было для .ini-подобных файлов. Подветка, начинающаяся с сообщения Миши Бергала к формату, описанному eao197 ранее, не относится.
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Re[32]: Формат конфигов
От: Павел Кузнецов  
Дата: 24.06.05 01:45
Оценка: 1 (1) -2
Здравствуйте, VladD2, Вы писали:

ПК>>Там два случая: один — серверный, второй — клиентский. Клиент кросс-платформенный, в нем используется реализация JavaScript из Mozilla (JSRef). Чтоб туда что-нибудь "прикрутить", нужно писать набор переходников в C++.


VD>Не надо песен, а... Для той же мозилы и его скриптов уже давно есть ХМЛ-парсеры.


Так... А ну бегом разбираться в том, как встраивается JSRef в сторонние приложения, и входит ли в комплект JSRef парсер XML...

ПК>> Вопрос: зачем, если конфиги в стиле .ini-файлов прекрасно работают?


VD>Это очередная подмена понятий. Об ini-файлах речь никто не вел. Речь велась о клепании влосипедов.


Могу только порекомендовать внимательнее следить за поворотами дискуссии...
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Re[25]: Формат конфигов
От: Шахтер Интернет  
Дата: 24.06.05 02:15
Оценка: +1
Здравствуйте, eao197, Вы писали:

E>Здравствуйте, VladD2, Вы писали:


VD>>Здравствуйте, eao197, Вы писали:


E>>> и я там отметился как противник XML-я. Не вообще, а против его повсеместного насаждения.


VD>>Вот я тоже против его повсеместного насождения. Но это еще не означет, что нужно быть его полным противником


E>Т.е., если я считаю, что для конфигурационных файлов есть более удобные форматы, то я являюсь полным противником XML?


VD>> и бороться за малопонятные велосипеды основания для применения нет.


E>Ну а то, что ты делаешь свой редактор вместо годами и многими проектами отлаженного редактора Scintilla -- это не изготовление малопонятного велосипеда?


Правильно делает. Скачай для интереса исходники Scintill ы и погляди на них даже без микроскопа.
Просто просятся на фабрику для вторсырья.


class Editor : public DocWatcher {
    // Private so Editor objects can not be copied
    Editor(const Editor &) : DocWatcher() {}
    Editor &operator=(const Editor &) { return *this; }

protected:    // ScintillaBase subclass needs access to much of Editor

    /** On GTK+, Scintilla is a container widget holding two scroll bars
     * whereas on Windows there is just one window with both scroll bars turned on. */
    Window wMain;    ///< The Scintilla parent window

    /** Style resources may be expensive to allocate so are cached between uses.
     * When a style attribute is changed, this cache is flushed. */
    bool stylesValid;
    ViewStyle vs;
    Palette palette;

    int printMagnification;
    int printColourMode;
    int printWrapState;
    int cursorMode;
    int controlCharSymbol;

    bool hasFocus;
    bool hideSelection;
    bool inOverstrike;
    int errorStatus;
    bool mouseDownCaptures;

    /** In bufferedDraw mode, graphics operations are drawn to a pixmap and then copied to
     * the screen. This avoids flashing but is about 30% slower. */
    bool bufferedDraw;
    /** In twoPhaseDraw mode, drawing is performed in two phases, first the background
    * and then the foreground. This avoids chopping off characters that overlap the next run. */
    bool twoPhaseDraw;

    int xOffset;        ///< Horizontal scrolled amount in pixels
    int xCaretMargin;    ///< Ensure this many pixels visible on both sides of caret
    bool horizontalScrollBarVisible;
    int scrollWidth;
    bool verticalScrollBarVisible;
    bool endAtLastLine;

    Surface *pixmapLine;
    Surface *pixmapSelMargin;
    Surface *pixmapSelPattern;
    Surface *pixmapIndentGuide;
    Surface *pixmapIndentGuideHighlight;

    LineLayoutCache llc;

    KeyMap kmap;

    Caret caret;
    Timer timer;
    Timer autoScrollTimer;
    enum { autoScrollDelay = 200 };

    Point lastClick;
    unsigned int lastClickTime;
    int dwellDelay;
    int ticksToDwell;
    bool dwelling;
    enum { selChar, selWord, selLine } selectionType;
    Point ptMouseLast;
    bool inDragDrop;
    bool dropWentOutside;
    int posDrag;
    int posDrop;
    int lastXChosen;
    int lineAnchor;
    int originalAnchorPos;
    int currentPos;
    int anchor;
    int targetStart;
    int targetEnd;
    int searchFlags;
    int topLine;
    int posTopLine;

    bool needUpdateUI;
    Position braces[2];
    int bracesMatchStyle;
    int highlightGuideColumn;

    int theEdge;

    enum { notPainting, painting, paintAbandoned } paintState;
    PRectangle rcPaint;
    bool paintingAllText;

    int modEventMask;

    SelectionText drag;
    enum { selStream, selRectangle, selRectangleFixed } selType;
    int xStartSelect;
    int xEndSelect;
    bool primarySelection;

    int caretXPolicy;
    int caretXSlop;    ///< Ensure this many pixels visible on both sides of caret

    int caretYPolicy;
    int caretYSlop;    ///< Ensure this many lines visible on both sides of caret

    int visiblePolicy;
    int visibleSlop;

    int searchAnchor;

    bool recordingMacro;

    int foldFlags;
    ContractionState cs;

    // Wrapping support
    enum { eWrapNone, eWrapWord } wrapState;
    int wrapWidth;
    int docLineLastWrapped;

    Document *pdoc;

    Editor();
    virtual ~Editor();
    virtual void Initialise() = 0;
    virtual void Finalise();

    void InvalidateStyleData();
    void InvalidateStyleRedraw();
    virtual void RefreshColourPalette(Palette &pal, bool want);
    void RefreshStyleData();
    void DropGraphics();

    virtual PRectangle GetClientRectangle();
    PRectangle GetTextRectangle();

    int LinesOnScreen();
    int LinesToScroll();
    int MaxScrollPos();
    Point LocationFromPosition(int pos);
    int XFromPosition(int pos);
    int PositionFromLocation(Point pt);
    int PositionFromLocationClose(Point pt);
    int PositionFromLineX(int line, int x);
    int LineFromLocation(Point pt);
    void SetTopLine(int topLineNew);

    bool AbandonPaint();
    void RedrawRect(PRectangle rc);
    void Redraw();
    void RedrawSelMargin();
    PRectangle RectangleFromRange(int start, int end);
    void InvalidateRange(int start, int end);

    int CurrentPosition();
    bool SelectionEmpty();
    int SelectionStart(int line=-1);
    int SelectionEnd(int line=-1);
    void SetSelection(int currentPos_, int anchor_);
    void SetSelection(int currentPos_);
    void SetEmptySelection(int currentPos_);
    bool RangeContainsProtected(int start, int end) const;
    bool SelectionContainsProtected() const;
    int MovePositionOutsideChar(int pos, int moveDir, bool checkLineEnd=true);
    int MovePositionTo(int newPos, bool extend=false, bool ensureVisible=true);
    int MovePositionSoVisible(int pos, int moveDir);
    void SetLastXChosen();

    void ScrollTo(int line, bool moveThumb=true);
    virtual void ScrollText(int linesToMove);
    void HorizontalScrollTo(int xPos);
    void MoveCaretInsideView(bool ensureVisible=true);
    int DisplayFromPosition(int pos);
    void EnsureCaretVisible(bool useMargin=true, bool vert=true, bool horiz=true);
    void ShowCaretAtCurrentPosition();
    void DropCaret();
    void InvalidateCaret();

    void NeedWrapping(int docLineStartWrapping=0);
    bool WrapLines();
    void LinesJoin();
    void LinesSplit(int pixelWidth);

    int SubstituteMarkerIfEmpty(int markerCheck, int markerDefault);
    void PaintSelMargin(Surface *surface, PRectangle &rc);
    LineLayout *RetrieveLineLayout(int lineNumber);
    void LayoutLine(int line, Surface *surface, ViewStyle &vstyle, LineLayout *ll,
        int width=LineLayout::wrapWidthInfinite);
    ColourAllocated TextBackground(ViewStyle &vsDraw, bool overrideBackground, ColourAllocated background, bool inSelection, int styleMain, int i, LineLayout *ll);
    void DrawIndentGuide(Surface *surface, int lineVisible, int lineHeight, int start, PRectangle rcSegment, bool highlight);
    void DrawEOL(Surface *surface, ViewStyle &vsDraw, PRectangle rcLine, LineLayout *ll,
        int line, int lineEnd, int xStart, int subLine, int subLineStart,
        bool overrideBackground, ColourAllocated background);
    void DrawLine(Surface *surface, ViewStyle &vsDraw, int line, int lineVisible, int xStart,
        PRectangle rcLine, LineLayout *ll, int subLine=0);
    void RefreshPixMaps(Surface *surfaceWindow);
    void Paint(Surface *surfaceWindow, PRectangle rcArea);
    long FormatRange(bool draw, RangeToFormat *pfr);
    int TextWidth(int style, const char *text);

    virtual void SetVerticalScrollPos() = 0;
    virtual void SetHorizontalScrollPos() = 0;
    virtual bool ModifyScrollBars(int nMax, int nPage) = 0;
    virtual void ReconfigureScrollBars();
    void SetScrollBars();
    void ChangeSize();

    void AddChar(char ch);
    virtual void AddCharUTF(char *s, unsigned int len, bool treatAsDBCS=false);
    void ClearSelection();
    void ClearAll();
        void ClearDocumentStyle();
    void Cut();
    void PasteRectangular(int pos, const char *ptr, int len);
    virtual void Copy() = 0;
    virtual bool CanPaste();
    virtual void Paste() = 0;
    void Clear();
    void SelectAll();
    void Undo();
    void Redo();
    void DelChar();
    void DelCharBack(bool allowLineStartDeletion);
    virtual void ClaimSelection() = 0;

    virtual void NotifyChange() = 0;
    virtual void NotifyFocus(bool focus);
    virtual int GetCtrlID() { return ctrlID; }
    virtual void NotifyParent(SCNotification scn) = 0;
    virtual void NotifyStyleToNeeded(int endStyleNeeded);
    void NotifyChar(int ch);
    void NotifyMove(int position);
    void NotifySavePoint(bool isSavePoint);
    void NotifyModifyAttempt();
    virtual void NotifyDoubleClick(Point pt, bool shift);
    void NotifyUpdateUI();
    void NotifyPainted();
    bool NotifyMarginClick(Point pt, bool shift, bool ctrl, bool alt);
    void NotifyNeedShown(int pos, int len);
    void NotifyDwelling(Point pt, bool state);
    void NotifyZoom();

    void NotifyModifyAttempt(Document *document, void *userData);
    void NotifySavePoint(Document *document, void *userData, bool atSavePoint);
    void CheckModificationForWrap(DocModification mh);
    void NotifyModified(Document *document, DocModification mh, void *userData);
    void NotifyDeleted(Document *document, void *userData);
    void NotifyStyleNeeded(Document *doc, void *userData, int endPos);
    void NotifyMacroRecord(unsigned int iMessage, uptr_t wParam, sptr_t lParam);

    void PageMove(int direction, bool extend=false);
    void ChangeCaseOfSelection(bool makeUpperCase);
    void LineTranspose();
    void LineDuplicate();
    virtual void CancelModes();
    void NewLine();
    void CursorUpOrDown(int direction, bool extend=false);
    int StartEndDisplayLine(int pos, bool start);
    virtual int KeyCommand(unsigned int iMessage);
    virtual int KeyDefault(int /* key */, int /*modifiers*/);
    int KeyDown(int key, bool shift, bool ctrl, bool alt, bool *consumed=0);

    int GetWhitespaceVisible();
    void SetWhitespaceVisible(int view);

    void Indent(bool forwards);

    long FindText(uptr_t wParam, sptr_t lParam);
    void SearchAnchor();
    long SearchText(unsigned int iMessage, uptr_t wParam, sptr_t lParam);
    long SearchInTarget(const char *text, int length);
    void GoToLine(int lineNo);

    char *CopyRange(int start, int end);
    void CopySelectionRange(SelectionText *ss);
    void SetDragPosition(int newPos);
    void DisplayCursor(Window::Cursor c);
    virtual void StartDrag();
    void DropAt(int position, const char *value, bool moving, bool rectangular);
    /** PositionInSelection returns 0 if position in selection, -1 if position before selection, and 1 if after.
     * Before means either before any line of selection or before selection on its line, with a similar meaning to after. */
    int PositionInSelection(int pos);
    bool PointInSelection(Point pt);
    bool PointInSelMargin(Point pt);
    void LineSelection(int lineCurrent_, int lineAnchor_);
    void DwellEnd(bool mouseMoved);
    virtual void ButtonDown(Point pt, unsigned int curTime, bool shift, bool ctrl, bool alt);
    void ButtonMove(Point pt);
    void ButtonUp(Point pt, unsigned int curTime, bool ctrl);

    void Tick();
    virtual void SetTicking(bool on) = 0;
    virtual void SetMouseCapture(bool on) = 0;
    virtual bool HaveMouseCapture() = 0;
    void SetFocusState(bool focusState);

    void CheckForChangeOutsidePaint(Range r);
    int BraceMatch(int position, int maxReStyle);
    void SetBraceHighlight(Position pos0, Position pos1, int matchStyle);

    void SetDocPointer(Document *document);

    void Expand(int &line, bool doExpand);
    void ToggleContraction(int line);
    void EnsureLineVisible(int lineDoc, bool enforcePolicy);
    int ReplaceTarget(bool replacePatterns, const char *text, int length=-1);

    int CodePage() const;

    virtual sptr_t DefWndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam) = 0;

public:
    // Public so the COM thunks can access it.
    bool IsUnicodeMode() const;
    // Public so scintilla_send_message can use it.
    virtual sptr_t WndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam);
    // Public so scintilla_set_id can use it.
    int ctrlID;
};

#endif
В XXI век с CCore.
Копай Нео, копай -- летать научишься. © Matrix. Парадоксы
Re[26]: Формат конфигов
От: Павел Кузнецов  
Дата: 24.06.05 03:00
Оценка: 2 (2) +1 -3 :))
Здравствуйте, Шахтер, Вы писали:

E>>Ну а то, что ты делаешь свой редактор вместо годами и многими проектами отлаженного редактора Scintilla -- это не изготовление малопонятного велосипеда?


Ш>Правильно делает. Скачай для интереса исходники Scintill ы и погляди на них даже без микроскопа. Просто просятся на фабрику для вторсырья.


Ш>
Ш><...>
Ш>



namespace RSharp.Compiler
{
    /// <summary>
    /// Компилятор/кодогенератор R#. Позволяет открыть проект VS или 
    /// набор фйлов R#/C#, осущесвить трансформацию и сгенерировать
    /// C#-файлы которые в последствии можно откомпилировать стандартным
    /// C#-компилятором.
    /// </summary>
    public class Compiler
    {
        #region Properties & variables.

        /// <summary>
        /// Подкаталог в каталоге определяемом свойством
        /// <see cref="RSharpSubfolder"/> в котором размещаются файлы
        /// содержащие метакод (код именяющий код).
        /// </summary>
        public const string MetaProjectSubfolder = "MetaCode";

        /// <summary>
        /// Подкаталог в каталоге определяемом свойством
        /// <see cref="RSharpSubfolder"/> в котором размещаются файлы
        /// генерируемые R#-ом конечный C#-файлы.
        /// </summary>
        public const string ProjectSubfolder = "Generated";

        private string _rSharpSubfolder = ".RSharp";

        /// <summary>
        /// Подкатало основного каталога проекта в котром размещаются
        /// служебные файлы R#-а.
        /// </summary>
        public string RSharpSubfolder
        {
            get { return _rSharpSubfolder; }
            set { _rSharpSubfolder = value; }
        }

        string _projectRoot;

        /// <summary>
        /// Директория проекта.
        /// </summary>
        public string ProjectRoot
        {
            get { return _projectRoot; }
            set { _projectRoot = value.ToLower(); }
        }


        private RProject _project;

        /// <summary>
        /// Позволяет получить AST проекта.
        /// </summary>
        protected RProject Project
        {
            get { return _project; }
        }

        private RProject _metaProject;

        /// <summary>
        /// Позволяет получить AST мета-проекта.
        /// </summary>
        protected RProject MetaProject
        {
            get { return _metaProject; }
        }

        protected Result _result;
        protected Parser _parser;
        protected MetaCodeBuilder _builder;

        #endregion Properties & variables.

        #region Загрузка AST

        /// <summary>
        /// Считывает файл проекта VS 2003 или 2005, парся 
        /// файлы входящие в прокт.
        /// </summary>
        /// <param name="projectPath">Путь к проекту VS.</param>
        /// <param name="parametrs">Дополнительные параметры.</param>
        public void LoadProjectFile(string projectPath, Parametrs parametrs)
        {
            projectPath = Path.GetFullPath(projectPath);
            string[] fileNames = GetProjectFiles(projectPath);
            this.ProjectRoot = Path.GetDirectoryName(projectPath);
            LoadFiles(fileNames, parametrs);
        }

        /// <summary>
        /// Позволяет загрузить список файлов как единый проект.
        /// </summary>
        /// <param name="fileNames">Список файлов проекта</param>
        /// <param name="parametrs">Параметры комиляции/кодогенерации</param>
        /// <returns></returns>
        public Result LoadFiles(string[] fileNames, Parametrs parametrs)
        {
            _result = new Result();

            // Изменям пути к файлам чтобы в дальнейшем небыло проблем 
            // при контекстной замене.
            MakePathLoverCase(ref fileNames);

            _project = null;
            PerfCounter timer = new PerfCounter(); // Изсеняем время загрузки.
            timer.Start();

            // Создаем прокт в который будут подключаться отпарсенные файлы.
            RProject project = new RProject();
            RProject metaProject = new RProject();
            string rsharpRoot = Path.Combine(this.ProjectRoot, "rsharp");
            project.ProjectRootPath = this.ProjectRoot;
            metaProject.ProjectRootPath = rsharpRoot;

            // Создаем и инициализируем парсер, сканер и объект-обработчик ошибок.

            if (_parser == null)
            {
                _parser = new Parser(new Scanner());

                // Подключаем обработку событий к парсеру.
                _parser.SemanticError += UserDefinedError;
                _parser.UserDefinedError += UserDefinedError;
            }

            _parser.DefineConstants.Clear();

            if (DefineConstants != null)
                _parser.DefineConstants.AddRange(DefineConstants);


            // Бежим по фйлам и прасим их...
            foreach (string fileName in fileNames)
            {
                try
                {
                    _parser.Scanner.InitFromFile(fileName);
                    _parser.Parse();

                    // Добавляем результат парсинга файла в кроект.
                    if (fileName.StartsWith(rsharpRoot))
                        metaProject.Add(_parser.CompileUnit);
                    else
                        project.Add(_parser.CompileUnit);
                }
                catch (Exception ex)
                {
                    _result.Errors.Add(new CompilerError(
                        fileName,
                        _parser.CurrentToken == null ? 0 : _parser.CurrentToken.Location.Line,
                        _parser.CurrentToken == null ? 0 : _parser.CurrentToken.Location.Col,
                        "Exception",
                        "Error during process loading project file: " + ex.Message));
                }
            }

            if (_result.Errors.Count > 0)
                _result.Output.Add("R#: Parsing filed with "
                    + _result.Errors.Count + " errors.");
            else
                _result.Output.Add("R#: Parse successfully by "
                    + timer.Finish() + " sec.");

            _project = project; // Запоминаем прокт для будущего использования.
            _metaProject = metaProject;

            return _result;
        }

        /// <summary>
        /// Позволяет подключить готовое AST.
        /// </summary>
        /// <param name="project">AST проекта.</param>
        /// <param name="metaProject">AST мета-проекта.</param>
        /// <param name="parametrs">
        /// Параметры компиляции/кодогенерации.
        /// </param>
        /// <returns></returns>
        public Result AttachAst(
            RProject project,
            RProject metaProject,
            Parametrs parametrs)
        {
            _project = project;
            _metaProject = metaProject;
            return new Result();
        }

        #endregion

        #region Формирование мета-кода (мета-правил).

        // Позволяет выделить мета-код, поместить его в отдельные файлы,
        // скомпилировать, и получить сборку содержащую мета-правила.
        public Result MakeMetaCode()
        {
            PerfCounter timer = new PerfCounter();
            timer.Start();

            string metaCodePath = Path.Combine(
                Path.Combine(this.ProjectRoot, this.RSharpSubfolder),
                MetaProjectSubfolder);

            if (!Directory.Exists(metaCodePath))
                Directory.CreateDirectory(metaCodePath);

            if (_builder == null)
                _builder = new MetaCodeBuilder();

            _builder.Parser = _parser;

            Result result = _builder.Init(_metaProject, metaCodePath, 
                this.ProjectRoot);

            result.Output.Add("R#: Metacode generetion & metaassembly builded successfully by "
                + timer.Finish() + " sec.");

            return result;
        }

        #endregion

        #region Трансформация кода проекта мета-правилами.

        // Позволяет произвести процесс преобразования.
        public Result Transform()
        {
            PerfCounter timer = new PerfCounter();
            timer.Start();

            string query = "/project/CompileUnits/compile-unit/AssemblyCustomAttributeSections/attribute-section/AttributeDeclarations";
            AstNodeCollection globalAttrs =
                XPathProvider.QueryNodes(_project, query);

            // Вызвать для каждого мета-правила соотвествующий IMetaRule
            IMetaRulesCaller metaRulesCaller = _builder.MetaRulesCaller;
            metaRulesCaller.Parser = _parser;
            metaRulesCaller.Project = _project;
            metaRulesCaller.MetaProject = _metaProject;
            metaRulesCaller.CallMetaRules(
                globalAttrs.ToArray<RAttributeDeclaration>());

            Result result = new Result();

            if (metaRulesCaller.Errors.Count > 0)
            {
                result.Errors.AddRange(metaRulesCaller.Errors);
                result.Output.Add("R#: Transformation execute failed with "
                    + metaRulesCaller.Errors.Count
                    + " errors.");
            }
            else
                result.Output.Add("R#: Transformation execute successfully by "
                    + timer.Finish() + " sec.");

            //TODO: Разобраться с new Result();
            return result;
        }

        #endregion

        #region Генерация C#-кода (по AST).

        // Позволяет сгенерировать C#-код из АСТ.
        public Result MakeCsCode()
        {
            PerfCounter timer = new PerfCounter();
            timer.Start();

            string genPath = Path.Combine(
                Path.Combine(this.ProjectRoot, this.RSharpSubfolder),
                ProjectSubfolder);

            if (!Directory.Exists(genPath))
                Directory.CreateDirectory(genPath);

            foreach (RCompileUnit unit in _project.CompileUnits)
            {
                string fileName = this.ProjectRoot.Length == 0
                    ? Path.Combine(genPath, unit.FileName)
                    : unit.FileName.Replace(this.ProjectRoot,
                    Path.GetFullPath(genPath));

                fileName = fileName.Replace(@"..\", "");

                Utilits.SaveToFile(unit, fileName);
            }

            RSharp.Utils.WriteFile(Path.Combine(genPath, "Info.txt"),
                string.Format(
                "Code generetion successfully at {0}." + Environment.NewLine
                + "{1} files generated." + Environment.NewLine
                + "{2} AST nones processed.",
                DateTime.Now.ToString(),
                _project.CompileUnits.Count,
                IdGenerator.Next() - 1));

            Result result = new Result();
            result.Output.Add("R#: C# code generetion successfully by "
                + timer.Finish() + " sec.");
            return result;
        }

        #endregion

        #region Work function

        public string[] GetProjectFiles(string projectPath)
        {
            // Открываем диалог выбора фйла проекта.

            string dir = Path.GetDirectoryName(projectPath);

            //if (dir.Length == 0)
            //    dir = Directory.GetCurrentDirectory();

            // Файл проекта - это XML-файл содержащий ряд списков. Их проще 
            // всего читать датасетом. По этому читаем файл в датасет и 
            // выскребаем нужную нам информацию.
                    
            DataSet ds = new DataSet();
            try
            {
                ds.ReadXml(projectPath);
            }
            catch (XmlException ex)
            {
                throw new ApplicationException(
                    string.Format("The file '{0}' not conform MS Visual Studio 2003/2005 "
                    + "or MSBuild project format.", projectPath), ex);
            }

            // Список файлов хранится в таблице "File".
            DataTable dt = ds.Tables["File"];

            if (dt == null)
                return ReadVs80Format(projectPath);
            else
                return ReadVs71Format(dt, dir);
        }

        private string _currentConfiguration;
        public const string Configuration = "Configuration";

        /// <summary>
        /// Текущая конфигурация.
        /// Если установить свойство в null, то при считывании
        /// проекта в него бедт считано текущее значение.
        /// </summary>
        /// <value></value>
        public string CurrentConfiguration
        {
            get { return _currentConfiguration; }
            set { _currentConfiguration = value; }
        }

        private string[] _loadItemTypes = { "Compile" };

        /// <summary>
        /// Список типов "Item-ов" которые нужно загружать из проектов
        /// MSBuild (VS 2005).
        /// По умолчанию он состоит только из "Compile".
        /// Более подробно о Item-ах см. документацию на MSBuild MSDN.
        /// </summary>
        public string[] LoadItemTypes
        {
            get { return _loadItemTypes; }
            set { _loadItemTypes = value; }
        }

        private string[] _defineConstants;

        /// <summary>
        /// Список предопределенных define-ов. Напрмер, "DEBUG" и "TRACE".
        /// </summary>
        public string[] DefineConstants
        {
            get { return _defineConstants; }
            set { _defineConstants = value; }
        }

        private static string GetMSBuildPropertyValue(
            MsBuildProjectHelper msbuild,
            string propertyName)
        {
            BuildProperty prop = msbuild.Project.EvaluatedProperties[propertyName];
            if (prop == null)
                return "";

            return prop.Value;
        }

        protected string[] ReadVs80Format(string projectPath)
        {
            string dir = Path.GetDirectoryName(projectPath);
            MsBuildProjectHelper msbuild = new MsBuildProjectHelper(projectPath);
            
            // Если конфигурация принятая по умолчанию не совпадает с 
            // CurrentConfiguration, то устанавливаем конфигурацию.
            // Это приведет к тому, что будет считаны файлы и настройки именно этой
            // конфигурации.
            if (CurrentConfiguration == null)
                CurrentConfiguration =
                    GetMSBuildPropertyValue(msbuild, Configuration);
            else
            {
                // Получаем значение конфигурации принятой по умолчанию.
                string configuration = GetMSBuildPropertyValue(msbuild, Configuration);

                if (configuration != CurrentConfiguration)
                    msbuild.Project.SetProperty(Configuration, "CurrentConfiguration", "");
            }

            if (DefineConstants == null)
            {
                // Считываем предопределенные define-ы.
                string dc = GetMSBuildPropertyValue(msbuild, "DefineConstants");
                DefineConstants = dc.Split(';');
            }

            // Считываем и филтруем список файлов проекта.

            List<string> files = new List<string>(300); // все равно ему помирать :)

            foreach (string filePath in msbuild.GetProjectFiles(LoadItemTypes))
            {
                if (CheckCsFile(filePath))
                    files.Add(Path.Combine(dir, filePath));
            }

            return files.ToArray();
        }

        protected static string[] ReadVs71Format(DataTable dt, string dir)
        {
            // В этот массив будут добавлены файлв входящие в проект.
            List<string> files = new List<string>(dt.Rows.Count);

            // Получаем идентификаторы колонок содержащие нужную нам информацию.
            int relPathIndex = dt.Columns["RelPath"].Ordinal;
            int subTypeIndex = dt.Columns["SubType"].Ordinal;
            int linkIndex = dt.Columns["Link"] == null
                    ? -1 : dt.Columns["Link"].Ordinal;

            // Пробегаемся по таблие и читаем информацию о файлах входящих 
            // в прокт.
            foreach (DataRow row in dt.Rows)
            {
                // Относительный путь.
                string relPath = (string)row[relPathIndex];
                // Подтип файла
                string subType = row[subTypeIndex] == DBNull.Value
                        ? null : (string)row[subTypeIndex];

                // Если это C#-ный файл и это не автогенеренный файл...
                if (subType == "Code" && CheckCsFile(relPath))
                {
                    // ... выясняем является ли файл ссылкой на файл из дугого 
                    // проекта или файлом входящим в прокт.

                    string link = linkIndex < 0 || row[linkIndex] == DBNull.Value
                            ? null : (string)row[linkIndex];

                    // Формируем полный путь к файлу.
                    if (link != null)
                        files.Add(Path.Combine(dir, link));
                    else
                        files.Add(Path.Combine(dir, relPath));
                }
            }

            return files.ToArray();
        }

        protected static bool CheckCsFile(string filePath)
        {
            return string.Compare(Path.GetExtension(filePath), ".cs", true) == 0
                            && !filePath.EndsWith(".gen.cs");
        }
        void MakePathLoverCase(ref string[] paths)
        {
            int len = paths.Length;
            string[] newPaths = new string[len];
            for (int i = 0; i < len; i++)
            {
                string path = paths[i];
                newPaths[i] = Path.Combine(
                        Path.GetDirectoryName(path).ToLower(),
                        Path.GetFileName(path));
            }

            paths = newPaths;
        }

        /// <summary>
        /// Обработчик ошибок. Помещает ошибку компиляции в список ошибок.
        /// </summary>
        protected void UserDefinedError(int line, int col, string error)
        {
            _result.Errors.Add(new CompilerError(
                    _parser.Scanner.FileName,
                    line,
                    col,
                    "err",
                    error));
        }

        #endregion
    }
}
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Re[14]: Формат конфигов
От: Andrei N.Sobchuck Украина www.smalltalk.ru
Дата: 24.06.05 07:42
Оценка:
VD>Что до инифайлов, то они конечно хороши... до тех пор пока информация умещается в рамках идиомы словаря. А как только данные в конфиге становятся иерархическими (или вообще грфом), то инифайлы становятся сущим адом. И намного проще будет обучить пользователя правть ХМЛ, чем

С графами в xml намного лучше чем в .ini?
http://www.smalltalk.ru | RSDN@Home 1.1.4 beta 7 rev. 447
Я ненавижу Hibernate
Автор: Andrei N.Sobchuck
Дата: 08.01.08
!
Re[12]: Формат конфигов
От: eao197 Беларусь http://eao197.blogspot.com
Дата: 24.06.05 07:45
Оценка:
Здравствуйте, VladD2, Вы писали:

E>>Кстати, я не изобретал синтаксис -- он уже был изобретен, причем даже не авторами Курла. Я просто сделал парсер этого формата (уже четыре года тому) и использую его для работы с конфигами и подобными вещами. Более того, когда в 2001 году я узнал про Курл, я написал одному из разработчиков Курла о том, что для конфигов Курл удобнее XML (на примере конфига для Java Web Application). И со мной согласились.


VD>Ну, вот теперь попробуй обдуать (я уж и не говорю сделать) что нужно добавить к твоему велосипеду, чтобы сделать универсальный ГУИ для его редактирования. Потом откой Янус... откой его настройки и погляди как редактируется ХМЛ в нем. Потом открой исходники януса и погляди как этот "ХМЛ" вглядит в коде. Думаю ты будешь удивлен, что это просто объект. Потом погляди на объем кода и попробуй оценить перерасход своих сил и потерю от не использования готовых решений и стандартов.


Этот формат хорош тем, что он гораздо читабельнее XML и его правка не требует навороченных визуальных редакторов.

Кроме того, я говорю про C++. Ситуация с Янусом совсем другая, т.к. для C# есть мощная поддержка XML-сериализации со стороны framework-а.
... << RSDN@Home 1.1.4 beta 7 rev. 447>>


SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Re[26]: Формат конфигов
От: eao197 Беларусь http://eao197.blogspot.com
Дата: 24.06.05 07:45
Оценка:
Здравствуйте, VladD2, Вы писали:

VD>Здравствуйте, eao197, Вы писали:


E>>Т.е., если я считаю, что для конфигурационных файлов есть более удобные форматы, то я являюсь полным противником XML?


VD>Ты предпочиташь написать велосипд с самопальным форматом вместо того чтобы воспользоваться почти готовым решением. Это твоя огромная проблема. И ХМЛ тут всего лишь частный случай.


Формат не самопальный. Велосипед уже давно написан и активно используется в узких кругах, причем с очень низкой стоимостью вхождения в него. Для конфигурационных файлов, которые приходится править вручную, XML, имхо, далеко не самая лучшая альтернатива.

VD>Вот ПК тут потихоньку, ну, чтобы было о чем поспорить, постоянно подсовывает разговор о ini-файлах. Так вот по мне они ничем ХМЛ-лю не уступают... до тех пор пока не нжно хранить информацию более сложную чем поимиьивгый ассоциативный список. Но ты же не об этом ведешь речь? Ты предлагашь страгать свои велосипеды только ради банального микроскопического хранилища настроичных данных. Да еще убеждашь, что людей нужно учить твоему доморощенному формату только на основании того, что он тебе кажется более удобным.


Да, я считаю, что если какой-то подход для конкретной задачи удобнее и не дороже альтернатив, то его следует использовать. В моем подходе к парсингу конфигов я вижу следующие преимущества:
— удобочитаемый и простой формат, который реально легко осваивается админами;
— простой механизм парсинга в C++ подобных форматов. Хоть код и получается объемным, но это очень простой код, который так же легко осваивается (не впример XML с DTD и XSD);
— это мощный механиз парсинга. Если ты не заметил, то класс для представления тега {if} -- шаблонный. Один и тот же код применяется и для работы с std::string, и для работы с unsigned int. Точно так же его можно использовать для работы с int или более экзотическими форматами типа IPv4 или IPv6. В случае же с разбором DOM-XML потребуется много действий для преобразования значений из строк в конкретные типы данных (те же самые unsigned int, int, IPv4, IPv6).

VD>>> и бороться за малопонятные велосипеды основания для применения нет.


E>>Ну а то, что ты делаешь свой редактор вместо годами и многими проектами отлаженного редактора Scintilla -- это не изготовление малопонятного велосипеда?


VD>Это не сравнимые вещи. По твоей аналогии я делаю пасрер ХМЛ для дотнета, так как для него его еще нет. Я не изобретаю свой тип редактора. Я всего лишь реализую то что уже стало стандартом.


Нет, у меня другая аналогия. Вот если бы я сделал парсер XML для C++ -- это был бы чистой воды велосипед, т.к. этих парсеров для C++ (хороших и разных), пруд пруди. А вот ты еще один редактор для C# пишешь. Зачем-то.
... << RSDN@Home 1.1.4 beta 7 rev. 447>>


SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Re[35]: Формат конфигов
От: AndrewVK Россия http://blogs.rsdn.org/avk
Дата: 24.06.05 08:09
Оценка:
Здравствуйте, Павел Кузнецов, Вы писали:

ПК>Точно также, например, как выбор XML для скриптов NAnt является откровенно неудачной идеей: выражение императивных конструкций через декларативные представляет собой достаточно жалкое зрелище.


Можно поподробнее про императивные конструкции в NAnt? Если ты про <if>, то такие конструкции есть и в чисто функциональных языках.
... << RSDN@Home 1.2.0 alpha rev. 499>>
AVK Blog
Re[33]: Формат конфигов
От: AndrewVK Россия http://blogs.rsdn.org/avk
Дата: 24.06.05 08:09
Оценка:
Здравствуйте, vdimas, Вы писали:

V>Тут правильно подметили, что даже при наличии готового XML-парсера, разбор конфигурационных файлов — не самая простая задача. Особенно если есть уровни вложенности. Вряд ли будет экономнее, чем то, что приведено в самописном парсере.


Если воспользоваться XmlSerializer, то безусловно экономнее, потому что разбирать руками при этом вобще ничего не нужно.

V>Для сравнения можно посмотреть в рефлекторе ту гору кода, которая пляшет вокруг довольно-таки строго размеченного app.config в .Net.


Он не строго размеченный, он вобще на 90% реконфигурируемый.

V> И это при том, что формат app.config с его секциями и именами классов — далеко не user-friendly, скорее наоборот. На "человеческий" config вся эта белиберда мало похожа.


А по мне так более чем. Ничуть не сложнее, скажем, конфигов апача. (Если кто опять прицепится к словам — это опять же намек, а не аргумент).
... << RSDN@Home 1.2.0 alpha rev. 499>>
AVK Blog
Re[15]: Формат конфигов
От: AndrewVK Россия http://blogs.rsdn.org/avk
Дата: 24.06.05 08:20
Оценка:
Здравствуйте, Andrei N.Sobchuck, Вы писали:

ANS>С графами в xml намного лучше чем в .ini?


http://www.w3.org/TR/xlink/
... << RSDN@Home 1.2.0 alpha rev. 499>>
AVK Blog
Re[18]: Формат конфигов
От: eao197 Беларусь http://eao197.blogspot.com
Дата: 24.06.05 08:51
Оценка: +1
Здравствуйте, VladD2, Вы писали:

VD>Здравствуйте, eao197, Вы писали:


E>>Влад, говоря про XML ты как бы автоматически подразумеваешь, что C++ код по извлечению данных из XML автоматически не может быть дремучим. Он просто обязательно будет компактным, понятным и сопровождаемым. Просто по определению.


VD>Говоря про ХМЛ, я говорю об использовании (повтором и многокртаном) проверенных решений и надежных библиотек. Даже С++ становится не стольк ужасным выбором, если в нем есть отлаженная библиотека для решения конкретной задачи.


Влад, я не подвергал сомнению то, что какой-нибудь TinyXML успешно и без проблем (без: кодировка utf-8 в TinyXml ?
Автор: Amouse
Дата: 20.06.05
) поднимет конфиг в DOM-представление. Я сомневаюсь, что код по извлечению этого конфига из DOM-представления будет по-определению компактным, понятным и сопровождаемым.
... << RSDN@Home 1.1.4 beta 7 rev. 447>>


SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Re[27]: Формат конфигов
От: Andy Panda США  
Дата: 24.06.05 12:00
Оценка:
Здравствуйте, Павел Кузнецов, Вы писали:

ПК>Здравствуйте, Шахтер, Вы писали:


E>>>Ну а то, что ты делаешь свой редактор вместо годами и многими проектами отлаженного редактора Scintilla -- это не изготовление малопонятного велосипеда?


Ш>>Правильно делает. Скачай для интереса исходники Scintill ы и погляди на них даже без микроскопа. Просто просятся на фабрику для вторсырья.


Ш>>
Ш>><...>
Ш>>


ПК>

ПК>>
ПК>><...>
ПК>>


?
... << RSDN@Home 1.1.4 beta 6a rev. 436>>
Re[28]: Формат конфигов
От: Павел Кузнецов  
Дата: 24.06.05 12:28
Оценка: +1
Здравствуйте, Andy Panda, Вы писали:

E>>>> Ну а то, что ты делаешь свой редактор вместо годами и многими проектами отлаженного редактора Scintilla -- это не изготовление малопонятного велосипеда?


Ш>>> Правильно делает. Скачай для интереса исходники Scintill ы и погляди на них даже без микроскопа. Просто просятся на фабрику для вторсырья.


Ш>>>
Ш>>><...>
Ш>>>


ПК>>

ПК>>>
ПК>>><...>
ПК>>>


AP>?


Гм... Это пример исходников из R#. Не думаю, что исходники обсуждаемого редактора будут сильно отличаться по исполнению...
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Re[13]: Формат конфигов
От: IT Россия linq2db.com
Дата: 24.06.05 13:46
Оценка:
Здравствуйте, eao197, Вы писали:

E>Кроме того, я говорю про C++. Ситуация с Янусом совсем другая, т.к. для C# есть мощная поддержка XML-сериализации со стороны framework-а.


Для плюсов и всего остального есть msxml.dll.
... << RSDN@Home 1.1.4 beta 7 rev. 447>>
Если нам не помогут, то мы тоже никого не пощадим.
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.