Форум программистов, компьютерный форум, киберфорум
C++: COM, OLE, ActiveX
Войти
Регистрация
Восстановить пароль
 
 
Рейтинг 4.74/58: Рейтинг темы: голосов - 58, средняя оценка - 4.74
19 / 18 / 1
Регистрация: 10.01.2017
Сообщений: 944
1

Чтение/запись в Excel с COM OLE C++

24.01.2019, 21:21. Просмотров 11394. Ответов 70
Метки нет (Все метки)

Здравствуйте,

Подскажите пожалуйста, есть ли у кого пример кода чтения и записи данных из/в откртый ексель файл с помощью COM/OLE на чистом С++.

Есть микрософтовский пример:
https://support.microsoft.com/... -or-import

Он создает ексель файл и записывает в него данные, но как записать данные в уже открытый ексель, как этот ексель файл отловить, такого примера нет, а доработать/изменить микрософтовский примера под свою задачу, для меня на данный момент, к сожалению совершенно не возможно, настолько там все сложно.

Может кто нибудь делал нечто подобное.
0
Programming
Эксперт
94731 / 64177 / 26122
Регистрация: 12.04.2006
Сообщений: 116,782
24.01.2019, 21:21
Ответы с готовыми решениями:

Cpp builder-Ole-Excel. WideString пишет в ячейку Excel минус перед нулем
Здравствуйте! Посмотрите пожалуйста: Когда аргумент val == 0, в ячейку Excel пишется "-0". Без...

Общение с Excel через OLE
Недавно начал изучать с++. Использую ms vc++ 2010 express. Код такой // 2.cpp: определяет точку...

OLE. Как переименовать созданный файл в Excel.Application?
Доброго дня всем! Подскажите, как переименовать созданный файл в Excel? По умолчанию создается...

Задание диапазона ячеек при объединении (OLE-сервер Excel)
Добрый день. Столкнулась с такой проблемой: при работе с OLE-сервером Excel, при задании диапазона...

70
19 / 18 / 1
Регистрация: 10.01.2017
Сообщений: 944
22.02.2019, 20:18  [ТС] 21
Простым опытным путем выяснил, что:

C++
1
2
3
4
5
//Get value from the range
    VARIANT rngResult;
    AutoWrap(DISPATCH_PROPERTYGET, &rngResult, pXlRange, (LPOLESTR)L"Value", 0);
 
        BYTE ival_byte = rngResult.bVal;
Не знаю, что за значение выдает этот BYTE, но, если формат ячейки ексель чисто текстовый, то если ival_byte вывести в консоль, то выдается "М", числовой же формат, общий или процентный, выдает пустое поле, то есть по идее можно было бы определять текстовый ли формат в ячейке или нет, простым условием:
C++
1
2
3
4
if (ival_byte == "M")
    {
        cout << "ival_byte = M"<<endl;
    }
Но тут ошибка, которую пока что не могу понять - "несовместимые типы операндов (BYTE и const char*)", то есть понятно уже, что их нельзя сравнивать, вопрос в том, как все таки можно их сравнить правильно ?
0
Модератор
3326 / 2111 / 339
Регистрация: 13.01.2012
Сообщений: 8,202
22.02.2019, 20:23 22
Optimus11, способ плохой. Сделайте то что хотите в VBA - там можно и список функций и свойств посмотреть и справку по ним почитать - потом перенесите в C++
0
19 / 18 / 1
Регистрация: 10.01.2017
Сообщений: 944
22.02.2019, 20:29  [ТС] 23
Прошу прощение, решил самым простым способом:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
//Get value from the range
    VARIANT rngResult;
    AutoWrap(DISPATCH_PROPERTYGET, &rngResult, pXlRange, (LPOLESTR)L"Value", 0);
 
        BYTE ival_byte = rngResult.bVal;
 
        bool I_byte = ival_byte;
 
    cout << "bool:" << I_byte << endl;
 
    if (I_byte == 1)
    {
        cout << "CEll Excel = text"<<endl;
    }
2)А как код VBA перенести в C++ ? Это же совершенно разный код ?

PS: Мне смотреть формат ячейки ексель нужно для того, что, когда я "читаю" значения из екселя в виде текста BSTR и если в этот момент в ясейке ексель находится не текст, а число, то происходит краш приложения, поэтому и хочу опредеять формат ячейки, если текстовый, то применять BSTR, если числовой, то LONG, а потом уже ковентировать в string/
0
Модератор
3326 / 2111 / 339
Регистрация: 13.01.2012
Сообщений: 8,202
23.02.2019, 21:09 24
Лучший ответ Сообщение было отмечено Optimus11 как решение

Решение

Optimus11, еще раз - это плохой способ - просто потому что значение хранимое в ячейке это совсем не формат ячейки. как быть?
1) запускаете Excel
2) включаете запись макроса
3) делаете нужные вам действия, например, устанавливаете формат ячейки в текстовый
4) выключаете запись макроса
5) идете в VBA (Alt + F11), открываете модуль в который записался макрос и читаете его содержимое, у меня оно примерно такое:
Visual Basic
1
2
    Range("E7").Select
    Selection.NumberFormat = "@"
что это означает? это означает что мы выделили ячейку и установили ее свойство NumberFormat. очевидно если мы хотим узнать что там за формат мы должны прочесть это свойство. так же очевидно, что у этого свойства есть некие значения соответствующие неким понимаемым человеком форматам. попробуем выделить слово NumberFormat и нажать F1 - видим справку и кое-какие пояснения о форматах. однако, справка сейчас совсем не та что была раньше - то ли сказывается падение уровня образования пользователей в результате которого справку вынуждены оглуплять то ли просто ее отрефакторили криворукие уроды. возможно придется вбить слово NumberFormat в поиск и почитать что пишут об этом продвинутые пользователи. в общих словах в NumberFormat хранится VARIANT в котором сидит текст-маска в том виде в каком мы его видим когда выбираем один из форматов.

Добавлено через 1 час 25 минут
Optimus11, если проблема именно в том что не понятно что лежит в ячейке то можно поступать как то так
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
            if (V_VT(&v) == VT_R8) //в ячейке значение типа VT_R8 типичного для вычислений в Excel
            {
                double x = V_R8(&v);
                //делаем что-то с x...
            }
            else if (V_VT(&v) == VT_BSTR) //в ячейке текст
            {
                BSTR x = V_BSTR(&v);
                //делаем что-то с x...
            }
            else //в ячейке что-то ужасное - пытаемся получить из этого текст
            {
                VARIANT tmp;
                VariantInit(&tmp);
 
                HRESULT hr = VariantChangeType(&tmp, &v, 0, VT_BSTR);
                if (hr != S_OK)
                {
                    //не вышло
                }
                else
                {
                    BSTR x = V_BSTR(&tmp);
                    //делаем что-то с x...
                }
 
                VariantClear(&tmp);
            }
1
19 / 18 / 1
Регистрация: 10.01.2017
Сообщений: 944
27.02.2019, 21:29  [ТС] 25
vxg, Огромное спасибо! Это прям, то что нужно.
0
19 / 18 / 1
Регистрация: 10.01.2017
Сообщений: 944
05.03.2019, 19:38  [ТС] 26
Здравствуйте,
Не могу разобраться, как можно выбирать рабочий лист екселя, а не только использовать активный ?

Есть из msdn`овского примера, вот эти строки:

C++
1
2
3
4
5
6
7
8
// Get ActiveSheet object
    IDispatch *pXlSheet;
    {
    VARIANT result;
    VariantInit(&result);
    AutoWrap(DISPATCH_PROPERTYGET, &result, pXlApp, (LPOLESTR)L"ActiveSheet", 0);//ActiveSheet
    pXlSheet = result.pdispVal;
    }
То есть результат выполнения этого кусочка кода pXlSheet = result.pdispVal и есть имя листа, но только активного.

Но, какая строка в autowrape, куда все эти параметры передаются, может отвечать за выбор нужно листа, а не активного, что то вообще ни как не могу понять.
0
19 / 18 / 1
Регистрация: 10.01.2017
Сообщений: 944
06.03.2019, 13:23  [ТС] 27
Решено добавлением еще одно параметра в AutoWrap:
C++
1
2
3
4
5
6
7
8
// Get ActiveSheet object
    IDispatch *pXlSheet;
    {
    VARIANT result;
    VariantInit(&result);
    AutoWrap(DISPATCH_PROPERTYGET, &result, pXlApp, (LPOLESTR)L"ActiveSheet", 0, [B]Name_sheet[/B]);//ActiveSheet
    pXlSheet = result.pdispVal;
    }
Где Name_sheet - соотвесвенно имя нужного листа. И все работает!
Но, я совершенно не понимаю, как это работает.
Autowrap - это функция, которая, если я правильно понимаю принимает в себя 5 параметров, но если смотреть, то Name_sheet - это 6ой параметр, как он туда тогда передается ?:

C++
1
2
3
4
HRESULT AutoWrap(int autoType, VARIANT *pvResult, IDispatch *pDisp, LPOLESTR ptName, int cArgs...)
{
...код...
}
0
Модератор
3326 / 2111 / 339
Регистрация: 13.01.2012
Сообщений: 8,202
06.03.2019, 18:17 28
Optimus11, есть такая штука «функции с переменным количеством аргументов»
0
Модератор
3326 / 2111 / 339
Регистрация: 13.01.2012
Сообщений: 8,202
09.03.2019, 08:02 29
Optimus11
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
#define BREAK_ON_FAIL(x) if (FAILED(hr = x)) break;
 
...
 
struct xls_t
{
    VARIANT app;
    VARIANT wbs;
    VARIANT wb;
    VARIANT wss;
    VARIANT ws;
};
 
HRESULT proc_beg(const std::string &path, xls_t * const xls, bool visible)
{
    HRESULT hr;
 
    VARIANT x;
    VARIANT _path;
 
    VariantInit(&xls->app);
    VariantInit(&xls->wbs);
    VariantInit(&xls->wb);
    VariantInit(&_path);
    VariantInit(&xls->wss);
    VariantInit(&xls->ws);
 
    while (true) {
 
    CoInitialize(NULL);
 
    CLSID clsid;
    BREAK_ON_FAIL(CLSIDFromProgID(L"Excel.Application", &clsid))
 
    xls->app.vt = VT_DISPATCH;
    xls->app.pdispVal = 0;
    BREAK_ON_FAIL(CoCreateInstance(clsid, NULL, CLSCTX_LOCAL_SERVER, IID_IDispatch, (void **)&xls->app.pdispVal))
 
    x.vt = VT_I4;
    x.lVal = visible;
    BREAK_ON_FAIL(AutoWrap(DISPATCH_PROPERTYPUT, NULL, xls->app.pdispVal, L"Visible", 1, x))
 
    BREAK_ON_FAIL(AutoWrap(DISPATCH_PROPERTYGET, &xls->wbs, xls->app.pdispVal, L"Workbooks", 0))
 
    if (path.size())
    {
        _path.vt = VT_BSTR;
        _path.bstrVal = SysAllocString(str_to_wstr(path).c_str());
        BREAK_ON_FAIL(AutoWrap(DISPATCH_METHOD, &xls->wb, xls->wbs.pdispVal, L"Open", 1, _path))
        VariantClear(&_path);
    }
    else
    {
        BREAK_ON_FAIL(AutoWrap(DISPATCH_METHOD, &xls->wb, xls->wbs.pdispVal, L"Add", 0))
    }
 
    BREAK_ON_FAIL(AutoWrap(DISPATCH_PROPERTYGET, &xls->wss, xls->wb.pdispVal, L"Worksheets", 0))
 
    x.lVal = 1;
    BREAK_ON_FAIL(AutoWrap(DISPATCH_PROPERTYGET, &xls->ws, xls->wss.pdispVal, L"Item", 1, x))
 
    break;}
 
    return hr;
}
 
HRESULT proc_end(HRESULT hr, xls_t * const xls, bool save, bool close)
{
    if (!FAILED(hr))
    {
        while (true) {
 
        if (save)
        {
            BREAK_ON_FAIL(AutoWrap(DISPATCH_METHOD, NULL, xls->wb.pdispVal, L"Save", 0))
        }
 
        if (close)
        {
            BREAK_ON_FAIL(AutoWrap(DISPATCH_METHOD, NULL, xls->wb.pdispVal, L"Close", 0))
        }
 
        break;}
    }
 
    VariantClear(&xls->app);
    VariantClear(&xls->wbs);
    VariantClear(&xls->wb);
    VariantClear(&xls->wss);
    VariantClear(&xls->ws);
 
    return hr;
}
 
HRESULT set_ws_no(xls_t * const xls, int no)
{
    HRESULT hr;
 
    VARIANT x;
 
    while (true) {
 
    x.vt = VT_I4;
    x.lVal = no;
    BREAK_ON_FAIL(AutoWrap(DISPATCH_PROPERTYGET, &xls->ws, xls->wss.pdispVal, L"Item", 1, x))
 
    break;}
 
    return hr;
}
0
19 / 18 / 1
Регистрация: 10.01.2017
Сообщений: 944
18.01.2020, 08:59  [ТС] 30
Здравствуйте,

Подскажите пожалуйста, вот есть к примеру:

C++
1
string data = "Hello";
С помощью com/winapi без проблем можно записать данные в ячейку ексель.

Но вопрос в другом, вот теперь есть к примеру:
C++
1
string data_unicode = u8"思";
То есть в string уже записывается не один байт информации под символ, а в данном случае три байта, соотвесвенно я все той же оберткой COM из записываю в ячейку ексель, НО ексель их воспринимает, как отдельные три символа в ascii кодировки видимо.

И вот вопрос, как сказать екселю, чтобы он данный ему массив data_unicode.c_str() - воспринимал как набор байт юникода и преобразовывал в символы юникод, а не по умолчанию в ascii ?
0
5842 / 3976 / 1630
Регистрация: 07.05.2019
Сообщений: 12,345
Записей в блоге: 1
18.01.2020, 11:12 31
Цитата Сообщение от Optimus11 Посмотреть сообщение
Но вопрос в другом, вот теперь есть к примеру:
string data_unicode = u8"思";
Это не юникод, это utf-8, немного разные вещи. Юникод-строка делается так
C++
1
std::wstring data_unicode = L"思";
И - excel работает только с юникодом.
1
19 / 18 / 1
Регистрация: 10.01.2017
Сообщений: 944
18.01.2020, 11:25  [ТС] 32
Цитата Сообщение от oleg-m1973 Посмотреть сообщение
Это не юникод, это utf-8, немного разные вещи. Юникод-строка делается так
C++
1
std::wstring data_unicode = L"思";
Насколько я понимаю юникод - это стандарт, а utf8 и utf16 его кодированные представления.
И буковка L если я правильно понимаю преобразует ascii символ или строку в utf16.

Цитата Сообщение от oleg-m1973 Посмотреть сообщение
И - excel работает только с юникодом.
Позвольте пожалуйста немного переформулировать вопрос:

Есть текстовый txt документ , в нем соотвесвенно ест текст в кодировке utf8, я его считывают getline'ом в string, и с помощью обертки COM записываю в ексель, но записываются абракадабры. Вопрос как записать тонда чтобы были нормальные символы ?
0
5842 / 3976 / 1630
Регистрация: 07.05.2019
Сообщений: 12,345
Записей в блоге: 1
18.01.2020, 11:41 33
Цитата Сообщение от Optimus11 Посмотреть сообщение
И буковка L если я правильно понимаю преобразует ascii символ или строку в utf16.
Именно это буковка тебе и нужна, при работе с BSTR.

Цитата Сообщение от Optimus11 Посмотреть сообщение
Есть текстовый txt документ , в нем соотвесвенно ест текст в кодировке utf8, я его считывают getline'ом в string, и с помощью обертки COM записываю в ексель, но записываются абракадабры. Вопрос как записать тонда чтобы были нормальные символы ?
Воспользуйся либо MultiByteToWideChar , CP_UTF8

Добавлено через 2 минуты
В общем, там надо будет по-любому конвертировать в юникод-строку, BSTR.
1
19 / 18 / 1
Регистрация: 10.01.2017
Сообщений: 944
18.01.2020, 11:51  [ТС] 34
Цитата Сообщение от oleg-m1973 Посмотреть сообщение
Воспользуйся либо MultiByteToWideChar , CP_UTF8
Если я правильно понимаю, то MultiByteToWideChar преобразует символ из одной кодировки в другую. Если так, то эта функция мне не нужна, так как в текстовом файле симвооы уже в ножной utf8 кодировке.
0
5842 / 3976 / 1630
Регистрация: 07.05.2019
Сообщений: 12,345
Записей в блоге: 1
18.01.2020, 15:35 35
Цитата Сообщение от Optimus11 Посмотреть сообщение
Если я правильно понимаю, то MultiByteToWideChar преобразует символ из одной кодировки в другую. Если так, то эта функция мне не нужна, так как в текстовом файле симвооы уже в ножной utf8 кодировке.
Нужной кому?
Она преобразовывает именно в то, что нужно передавать в excel. Или тебе не это надо?
1
19 / 18 / 1
Регистрация: 10.01.2017
Сообщений: 944
18.01.2020, 15:56  [ТС] 36
Цитата Сообщение от oleg-m1973 Посмотреть сообщение
Нужной кому?
Она преобразовывает именно в то, что нужно передавать в excel. Или тебе не это надо?
Я сам запутался уже

Я не совсем тогда понимаю, как это функция работает, как я понял на вход она принимает массив чаров в стиле строки Си, а на выходе то, что она выдает ?
0
5842 / 3976 / 1630
Регистрация: 07.05.2019
Сообщений: 12,345
Записей в блоге: 1
18.01.2020, 16:11 37
Лучший ответ Сообщение было отмечено Optimus11 как решение

Решение

Цитата Сообщение от Optimus11 Посмотреть сообщение
Я не совсем тогда понимаю, как это функция работает, как я понял на вход она принимает массив чаров в стиле строки Си, а на выходе то, что она выдает ?
На выходе выдаёт юникод-строку, utf-16.
C++
1
2
3
4
5
6
7
8
        setlocale(LC_ALL, "Russian");
        const char8_t s[] = u8"111фывапролФЫВАПРОЛ";
        wchar_t buf[1024];
        const auto res = ::MultiByteToWideChar(CP_UTF8, 0, (LPCCH)s, std::size(s), buf, std::size(buf));
        std::wcout << buf << std::endl;
 
        BSTR s2 = ::SysAllocString(buf); //Ready to send to excel
        std::wcout << s2 << std::endl;
Я правильно понимаю, что у тебя файл в utf-8?
1
19 / 18 / 1
Регистрация: 10.01.2017
Сообщений: 944
18.01.2020, 17:43  [ТС] 38
Цитата Сообщение от oleg-m1973 Посмотреть сообщение

Я правильно понимаю, что у тебя файл в utf-8?
Да.
0
19 / 18 / 1
Регистрация: 10.01.2017
Сообщений: 944
23.01.2020, 21:34  [ТС] 39
Спасибо!
Все это работает, но я уже голову сломал, не могу, откудато в появляется после преобразованного чимвола мусор. Причем муссор появляется не в буффере, потому как его размер просто ограничен ровно тем каюоличеством, которые нудны по сисвол utf16.
Мусор появляется уже в екселе почему то.
0
5842 / 3976 / 1630
Регистрация: 07.05.2019
Сообщений: 12,345
Записей в блоге: 1
23.01.2020, 21:39 40
Цитата Сообщение от Optimus11 Посмотреть сообщение
Все это работает, но я уже голову сломал, не могу, откудато в появляется после преобразованного чимвола мусор. Причем муссор появляется не в буффере, потому как его размер просто ограничен ровно тем каюоличеством, которые нудны по сисвол utf16.
Мусор появляется уже в екселе почему то.
Скорее всего, BSTR неправильно создаёшь. Покажи код.
0
IT_Exp
Эксперт
87844 / 49110 / 22898
Регистрация: 17.06.2006
Сообщений: 92,604
23.01.2020, 21:39

Заказываю контрольные, курсовые, дипломные и любые другие студенческие работы здесь.

Чтение из Excel в StringGrid
Здравствуйте! Как считать информацию из открытого файла Excel в StringGrid? Пробовала вот так: ...

Работа с БД SQLite 3: чтение, запись
есть бд SQLite 3, раньше с SQLite не работал, подскажите как от туда считать данные и записать их...

Чтение данных из Excel с Visual C++
Здраствуйте. Нужно запрограммировать считывание данных из Excel-файла на Visual C++ (MVS 2005)....

Запись и чтение Excel
Здраствуйте. Подскажите как реализировать возможность записывать информацию с полей в лист Excel....


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
40
Ответ Создать тему
Опции темы

КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2021, vBulletin Solutions, Inc.