Модераторы: Partizan, gambit
  

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Работа с Office2007 в C#.NET+совместимость с 2003 
:(
    Опции темы
n0rb
Дата 19.3.2008, 00:08 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 4
Регистрация: 28.2.2008

Репутация: нет
Всего: нет



Здравствуйте, коллеги.

Я разрабатываю .NET-приложение, одна из функций которого - генерация довольно сложных Word- и Excel-документов. Проблема в том, что у меня на машине стоит 2007 офис, а у заказчика 2003. Хотелось бы, чтобы работа с Office выполнялась идентично на обоих версиях (2003 и 2007), и при этом не используя дергание методов наподобие Activator.CreateInstance(Type.GetTypeFromProgID("Word.Application")), MethodInfo.Invoke("...."), BindingFlags и т.д., так как тормоза очень большие + ужасно неудобно при разработке.

В COM-референсах есть только 12-й офис, т.е. 2007. Насколько знаю, если у клиента 2003 - это не пройдет.
Я подумал - а можно ли, например, стянуть отдельные файлы библиотек для Office11 (что-то вроде DLL, TLB или OLB) и добавить в референсы проекта? Смогу ли я разрабатывать и тестировать проект на своей машине, имея 2007 офис, и пойдет ли это под 2003-й при таких манипуляциях? Может существует какой-нибудь другой выход (напоминаю, мне нужна именно совместимосить обоих версий)?

Заранее спасибо.
PM MAIL   Вверх
marcusmae
Дата 19.3.2008, 02:24 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


stravaganza
**


Профиль
Группа: Участник
Сообщений: 874
Регистрация: 26.3.2006

Репутация: 22
Всего: 39



n0rb, если я правильно понял, то ведь неважно, какая версия офиса, если и там, и там использовать один и тот же старый формат doc/xls (и не использовать новый docx/xlsx)? Тогда действительно, например, для работы с таблицами Excel можно "стянуть" COM-библиотеки Microsoft Jet 4.0 (Interop.ADODB.dll и Interop.ADOX.dll) от 2003-его и включить их в свой дистрибутив. Хотя можно без них обойтись и работать только на System.Data.OleDb. Проблема в том, что он работает медленно, поэтому сам лично предпочитаю некоторую смесь.

Загрузка :

Код

// (c) 2007 Mae Marcus - sample code

using x86Database.Interop.Info;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Data.OleDb;

namespace x86Database.Interop.v2003
{
    /// <summary>
    /// Defines Microsoft Office 2003 databases loading capabilities.
    /// </summary>
    public static class Load
    {
        /// <summary>
        /// Get the specified database data tables names.
        /// </summary>
        /// <param name="databasePath">The database path.</param>
        /// <returns>The data tables names array.</returns>
        public static string[] GetDataTablesNames(string databasePath)
        {
            // Tables names column index.
            const int tablesNamesColumnIndex = 2;

            OleDbConnection oleDbConnection = null;
            string[] tablesNames = null;
            bool tablesNamesFilled = false;
            try
            {
                oleDbConnection = new OleDbConnection(
                    Tools.GetConnectionString(databasePath));
                oleDbConnection.Open();

                DataTable schemaTable =
                    oleDbConnection.GetOleDbSchemaTable(OleDbSchemaGuid.Tables_Info, // modified from OleDbSchemaGuid.Tables
                    new object[] { null, null, null, "TABLE" });

                tablesNames = new string[schemaTable.Rows.Count];
                for (int index = 0; index < schemaTable.Rows.Count; index++)
                    tablesNames[index] = Convert.ToString(
                        schemaTable.Rows[index].ItemArray[tablesNamesColumnIndex]);
                tablesNamesFilled = true;
            }
            finally
            {
                if (oleDbConnection!=null) oleDbConnection.Close();
            }

            if ((tablesNames == null) || !tablesNamesFilled)
                throw new ArgumentException("Tables names uninitialized",
                          tablesNames.ToString());
            return tablesNames;
        }

        /// <summary>
        /// Get the data table from the specified database.
        /// </summary>
        /// <param name="databasePath">The database path.</param>
        /// <param name="tableName">The data table name.</param>
        /// <returns>The data table.</returns>
        public static DataTable GetDataTable(string databasePath, string tableName)
        {
            OleDbConnection oleDbConnection = null;
            DataTable dataTable = null;
            try
            {
                oleDbConnection = new OleDbConnection(
                    Tools.GetConnectionString(databasePath));
                oleDbConnection.Open();

                OleDbCommand oleDbCommand = new OleDbCommand(
                    String.Format("SELECT * FROM [{0}]", tableName),
                    oleDbConnection);

                OleDbDataAdapter oleDbDataAdapter = new OleDbDataAdapter(oleDbCommand);
                dataTable = new DataTable(tableName);
                oleDbDataAdapter.Fill(dataTable);
            }
            finally
            {
                if (oleDbConnection != null) oleDbConnection.Close();
            }

            if (dataTable != null) return dataTable;

            throw new ArgumentException("Failed to open a datatable.", dataTable.ToString());
        }

        /// <summary>
        /// Get the specified data table columns names.
        /// </summary>
        /// <param name="databasePath">The database path.</param>
        /// <param name="tableName">The data table name.</param>
        /// <returns>The data table columns names array.</returns>
        public static string[] GetDataTableColumnsNames(string databasePath, string tableName)
        {
            // Columns names column index.
            const int columnsNamesColumnIndex = 3;

            OleDbConnection oleDbConnection = null;
            string[] columnsNames = null;
            bool columnsNamesFilled = false;
            try
            {
                oleDbConnection = new OleDbConnection(
                    Tools.GetConnectionString(databasePath));
                oleDbConnection.Open();

                DataTable schemaTable =
                    oleDbConnection.GetOleDbSchemaTable(OleDbSchemaGuid.Columns,
                    new object[] { null, null, tableName, null });

                columnsNames = new string[schemaTable.Rows.Count];
                for (int index = 0; index < schemaTable.Rows.Count; index++)
                    columnsNames[index] = Convert.ToString(
                        schemaTable.Rows[index].ItemArray[columnsNamesColumnIndex]);
                columnsNamesFilled = true;
            }
            finally
            {
                if (oleDbConnection != null) oleDbConnection.Close();
            }

            if ((columnsNames == null) || !columnsNamesFilled)
                throw new ArgumentException("Tables names uninitialized",
                          columnsNames.ToString());
            return columnsNames;
        }

        /// <summary>
        /// Get the specified data table columns names.
        /// </summary>
        /// <param name="dataTable">The data table to get the columns names of.</param>
        /// <returns>The data table columns names array.</returns>
        public static string[] GetDataTableColumnsNames(DataTable dataTable)
        {
            string[] columnsNames = new string[dataTable.Columns.Count];
            for (int index = 0; index < columnsNames.Length; index++)
                columnsNames[index] = dataTable.Columns[index].Caption;
            return columnsNames;
        }

        /// <summary>
        /// Get the specified data table info.
        /// </summary>
        /// <param name="dataTable">The data table to get the info for.</param>
        /// <returns>The data table info instance.</returns>
        public static DataTableInfo GetDataTableInfo(DataTable dataTable)
        {
            string[] columnsNames = Load.GetDataTableColumnsNames(dataTable);
            return new DataTableInfo(dataTable.TableName, columnsNames);
        }

        /// <summary>
        /// Get the specified database info.
        /// </summary>
        /// <param name="databasePath">The database path.</param>
        /// <returns>The database info instance.</returns>
        public static DatabaseInfo GetDatabaseInfo(string databasePath)
        {
            string[] tablesNames = Load.GetDataTablesNames(databasePath);
            DataTableInfo[] dataTablesInfo = new DataTableInfo[tablesNames.Length];

            for (int tableIndex = 0; tableIndex < tablesNames.Length; tableIndex++)
            {
                dataTablesInfo[tableIndex] = new DataTableInfo(tablesNames[tableIndex],
                    Load.GetDataTableColumnsNames(databasePath,
                    tablesNames[tableIndex]));
            }

            return new DatabaseInfo(databasePath, dataTablesInfo, null);
        }

        /// <summary>
        /// Get the specified databases infos.
        /// </summary>
        /// <param name="databasesPaths">The databases paths array.</param>
        /// <returns>The databases infos array.</returns>
        public static DatabaseInfo[] GetDatabasesInfo(string[] databasesPaths)
        {
            DatabaseInfo[] result = new DatabaseInfo[databasesPaths.Length];

            for (int databaseIndex = 0; databaseIndex < result.Length; databaseIndex++)
                result[databaseIndex] = Load.GetDatabaseInfo(
                    databasesPaths[databaseIndex]);

            return result;
        }

        /// <summary>
        /// Get the data reader for the specified data column.
        /// </summary>
        /// <param name="databasePath">The database path.</param>
        /// <param name="tableName">The data table name.</param>
        /// <param name="columnName">The data column name.</param>
        /// <returns>The data reader for the specified data column.</returns>
        public static OleDbDataReader GetDataReader(string databasePath, string tableName,
            string columnName)
        {
            OleDbConnection oleDbConnection = new OleDbConnection(
                Tools.GetConnectionString(databasePath));
            oleDbConnection.Open();

            OleDbCommand oleDbCommand = new OleDbCommand(
                String.Format("SELECT `{0}` FROM `{1}`", columnName, tableName),
                oleDbConnection);

            return oleDbCommand.ExecuteReader();
        }

        /// <summary>
        /// Get the data enumerator for the specified data column.
        /// </summary>
        /// <param name="databasePath">The database path.</param>
        /// <param name="tableName">The data table name.</param>
        /// <param name="columnName">The data column name.</param>
        /// <returns>The data enumerator for the specified data column.</returns>
        /// <remarks>Uses ADODB and ADOX.</remarks>
        public static IEnumerator GetDataEnumerator(string databasePath,
            string tableName, string columnName)
        {
            ADODB.ConnectionClass connection = new ADODB.ConnectionClass();
            connection.Open(Tools.GetConnectionString(databasePath),
                null, null, 0);
            String commandString =
                String.Format("SELECT `{0}` FROM `{1}`", columnName, tableName);
            ADODB.RecordsetClass recordset = new ADODB.RecordsetClass();
            recordset.Open(commandString, connection,
                ADODB.CursorTypeEnum.adOpenForwardOnly,
                ADODB.LockTypeEnum.adLockReadOnly, 0);

            ADODB.Field columnField = recordset.Fields[0];
            while (!recordset.EOF)
            {
                yield return columnField.Value;
                recordset.MoveNext();
            }
            connection.Close();
        }

        /// <summary>
        /// Get the data enumerator for the specified data column.
        /// </summary>
        /// <param name="dataTable">The source data table.</param>
        /// <param name="columnName">The column name to get the enumerator for.</param>
        /// <returns>The data enumerator for the specified data column.</returns>
        public static IEnumerator GetDataEnumerator(DataTable dataTable,
            string columnName)
        {
            int columnIndex = dataTable.Columns.IndexOf(columnName);
            for (int rowIndex = 0; rowIndex < dataTable.Rows.Count; rowIndex++)
                yield return dataTable.Rows[rowIndex].ItemArray.GetValue(columnIndex);
        }
    }
}


Сохранение :

Код

// (c) 2007 Mae Marcus - sample code

using ADODB;
using ADOX;
using System;
using System.Data;
using System.IO;

namespace x86Database.Interop.v2003
{
    /// <summary>
    /// Defines Microsoft Office 2003 databases saving capabilities.
    /// </summary>
    public static class Save
    {
        public static void SetTable(DataTable dataTable, string databasePath)
        {
            // Create new catalog class.
            CatalogClass catalogClass = new CatalogClass();

            string connectString = Tools.GetConnectionString(
                databasePath) + ";Jet OLEDB:Engine Type=5";

            if (File.Exists(databasePath)) File.Delete(databasePath);

            catalogClass.Create(connectString);

            Save.SetTable(dataTable, catalogClass);

            ((Connection)catalogClass.ActiveConnection).Close();
        }

        public static void SetTables(DataTable[] dataTables, string databasePath)
        {
            // Create new catalog class.
            CatalogClass catalogClass = new CatalogClass();

            string connectString = Tools.GetConnectionString(
                databasePath) + ";Jet OLEDB:Engine Type=5";

            if (File.Exists(databasePath)) File.Delete(databasePath);

            catalogClass.Create(connectString);

            foreach (DataTable dataTable in dataTables)
                Save.SetTable(dataTable, catalogClass);

            ((Connection)catalogClass.ActiveConnection).Close();
        }

        private static void SetTable(DataTable dataTable, CatalogClass catalogClass)
        {
            TableClass tableClass = new TableClass();
            tableClass.Name = dataTable.TableName;
            tableClass.ParentCatalog = catalogClass;
            
            foreach (DataColumn dataColumn in dataTable.Columns)
            {
                Int32 size = dataColumn.MaxLength;
                if (dataColumn.MaxLength <= 0) size = 255;

                // adding new column
                ColumnClass columnClass = new ColumnClass();
                columnClass.Name = dataColumn.ColumnName;

                switch (dataColumn.DataType.ToString())
                {
                    case "System.Boolean":
                        columnClass.Type = ADOX.DataTypeEnum.adBoolean;
                        break;
                    case "System.Byte":
                        columnClass.Type = ADOX.DataTypeEnum.adUnsignedTinyInt;
                        break;
                    case "System.Char":
                        if (size > 255)
                            // use memo field if over 255
                            columnClass.Type = ADOX.DataTypeEnum.adLongVarWChar;
                        else
                        {
                            columnClass.Type = ADOX.DataTypeEnum.adVarWChar;
                            columnClass.DefinedSize = size;
                        }
                        columnClass.Properties[
                            "Jet OLEDB:Compressed UniCode Strings"].Value = true;
                        break;
                    case "System.DateTime":
                        columnClass.Type = ADOX.DataTypeEnum.adDate;
                        break;
                    case "System.Decimal":
                        columnClass.Type = ADOX.DataTypeEnum.adNumeric;
                        columnClass.Precision = 18;
                        columnClass.NumericScale = 4;
                        break;
                    case "System.Double":
                        columnClass.Type = ADOX.DataTypeEnum.adDouble;
                        break;
                    case "System.Int16":
                        columnClass.Type = ADOX.DataTypeEnum.adSmallInt;
                        break;
                    case "System.Int32":
                        columnClass.Type = ADOX.DataTypeEnum.adInteger;
                        break;
                    case "System.Int64":
                        // obvious problems with possible overflow
                        columnClass.Type = ADOX.DataTypeEnum.adInteger;
                        break;
                    case "System.SByte":
                        columnClass.Type = ADOX.DataTypeEnum.adUnsignedTinyInt;
                        break;
                    case "System.Single":
                        columnClass.Type = ADOX.DataTypeEnum.adSingle;
                        break;
                    case "System.String":
                        if (size > 255)
                            // use memo field if over 255
                            columnClass.Type = ADOX.DataTypeEnum.adLongVarWChar;
                        else
                        {
                            columnClass.Type = ADOX.DataTypeEnum.adVarWChar;
                            columnClass.DefinedSize = size;
                        }
                        break;
                    case "System.TimeSpan":
                        columnClass.Type = ADOX.DataTypeEnum.adDate;
                        break;
                    case "System.UInt16":
                        // no unsigned types in Access
                        columnClass.Type = ADOX.DataTypeEnum.adSmallInt;
                        break;
                    case "System.UInt32":
                        columnClass.Type = ADOX.DataTypeEnum.adInteger;
                        break;
                    case "System.Unt64":
                        columnClass.Type = ADOX.DataTypeEnum.adInteger;
                        break;
                    default:
                        columnClass.Type = ADOX.DataTypeEnum.adBinary;
                        break;
                }
                tableClass.Columns.Append(columnClass, columnClass.Type, size);

                if (dataColumn.DataType != typeof(System.Boolean))
                    if (dataColumn.AllowDBNull)
                        tableClass.Columns[columnClass.Name].Properties[
                            "Nullable"].Value = true;
                    else
                        tableClass.Columns[columnClass.Name].Properties[
                            "Nullable"].Value = false;

                if (dataColumn.AutoIncrement)
                {
                    tableClass.Columns[columnClass.Name].Properties[
                        "Autoincrement"].Value = true;
                    tableClass.Columns[columnClass.Name].Properties[
                        "Seed"].Value = dataColumn.AutoIncrementSeed;
                    tableClass.Columns[columnClass.Name].Properties[
                        "Increment"].Value = dataColumn.AutoIncrementStep;
                }
            }

            catalogClass.Tables.Append(tableClass);

            // Open an ADODB recordset to the table
            RecordsetClass recordSetClass = new RecordsetClass();
            Connection connectionClass =
                (Connection)catalogClass.ActiveConnection;
            recordSetClass.Open('[' + dataTable.TableName + ']', connectionClass,
                CursorTypeEnum.adOpenKeyset, LockTypeEnum.adLockOptimistic, 0);

            // Delete any existing data
            object missing = Type.Missing;
            connectionClass.Execute("delete from " + '[' + dataTable.TableName + ']',
                out missing, (int)CommandTypeEnum.adCmdText +
                (int)ADODB.ExecuteOptionEnum.adExecuteNoRecords);

            // copy the data
            string[] columnsIndexes = new string[dataTable.Columns.Count];
            for (int columnIndex = 0; columnIndex < dataTable.Columns.Count;
                columnIndex++) columnsIndexes[columnIndex] =
                    dataTable.Columns[columnIndex].Caption;

            for (int rowIndex = 0; rowIndex < dataTable.Rows.Count; rowIndex++)
            {
                object[] itemArray = dataTable.Rows[rowIndex].ItemArray;

                recordSetClass.AddNew(0, itemArray[0]);
                for (int columnIndex = 1; columnIndex < columnsIndexes.Length;
                    columnIndex++)
                {
                    recordSetClass.Fields[columnIndex].Value = itemArray[columnIndex];
                    recordSetClass.Update(columnIndex, itemArray[columnIndex]);
                }
            }
        }
    }
}



--------------------
ἀπὸ μηχανῆς θεός
PM MAIL ICQ GTalk   Вверх
n0rb
Дата 19.3.2008, 17:45 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 4
Регистрация: 28.2.2008

Репутация: нет
Всего: нет



marcusmae, спасибо за классы, но дело в том, что в этой ситуации мне наиболее важна работа с документами Word, т.е. замена текста, вставка таблиц, картинок, колонок и пр.
Для всего этого в VBA есть средства, но меня именно интересует, можно ли как-нибудь хитро использовать Office Class Library (или что-то другое), т.е. не прибегая к late binding, и при этом сохранить совместимость.

Цитата

неважно, какая версия офиса, если и там, и там использовать один и тот же старый формат doc/xls

это само собой, т.к. 2003-й не "понимает" формат Office XML, который был введен в 2007-й версии. Вопрос в том, как этот doc/xls документ сделать, чтобы он был совместим с 2003, когда у меня 2007 стоит.

Это сообщение отредактировал(а) n0rb - 19.3.2008, 18:13
PM MAIL   Вверх
marcusmae
Дата 19.3.2008, 19:46 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


stravaganza
**


Профиль
Группа: Участник
Сообщений: 874
Регистрация: 26.3.2006

Репутация: 22
Всего: 39



Цитата(n0rb @  19.3.2008,  17:45 Найти цитируемый пост)
Вопрос в том, как этот doc/xls документ сделать, чтобы он был совместим с 2003, когда у меня 2007 стоит.


Ну вроде это уже обсудили : можно сделать так, чтобы создание документа вообще не зависило от того, какой офис установлен на машине. Для этого нужно обеспечить дистрибутив своей программы необходимым набором COM-библиотек - не важно от какой версии офиса : 2003, 2007 или OpenOffice 2.0. Главное, чтобы Ваша программа умела с ними работать. Пара классов для Excel служат тому примером. Нужно что-то подобное для Word-а?


--------------------
ἀπὸ μηχανῆς θεός
PM MAIL ICQ GTalk   Вверх
  
Ответ в темуСоздание новой темы Создание опроса
Прежде чем создать тему, посмотрите сюда:
mr.DUDA
THandle

Используйте теги [code=csharp][/code] для подсветки кода. Используйтe чекбокс "транслит" если у Вас нет русских шрифтов.
Что делать если Вам помогли, но отблагодарить помощника плюсом в репутацию Вы не можете(не хватает сообщений)? Пишите сюда, или отправляйте репорт. Поставим :)
Так же не забывайте отмечать свой вопрос решенным, если он таковым является :)


Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, mr.DUDA, THandle.

 
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | Общие вопросы по .NET и C# | Следующая тема »


 




[ Время генерации скрипта: 0.0547 ]   [ Использовано запросов: 22 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.