Excel interop C++: Workbook SaveAs exception - Code 0x800A03EC - c++

I'm working with VBE6EXT and MSO to import/export excel files from my C++ application. I'm using the following code:
#import "C:\\Program Files (x86)\\Common Files\\Microsoft Shared\\OFFICE14\\MSO.DLL" \
rename("RGB", "MSORGB")
using namespace Office;
#import "C:\Program Files (x86)\\Common Files\\Microsoft Shared\\VBA\\VBA6\\VBE6EXT.OLB"
using namespace VBIDE;
#import "C:\\Program Files (x86)\\Microsoft Office\\Office14\\EXCEL.EXE" rename( "DialogBox", "ExcelDialogBox" ) \
rename( "RGB", "ExcelRGB" ) \
rename( "CopyFile", "ExcelCopyFile" ) \
rename( "ReplaceText", "ExcelReplaceText" ) \
exclude( "IFont", "IPicture" ) no_dual_interfaces
I've office 2010 installed, activated, up to date and I'm using Visual Studio 2015 Community Edition with Update 3 on Windows 10.
When I try to open a xlsx file and read value, everything works well, but when I try to create a file from scratch and save it, my program crash and throw a _com_error, with the following code:
Code :
Excel::_ApplicationPtr pApplication;
HRESULT hr = CoInitialize(nullptr);
if (FAILED(hr)) throw std::runtime_error("Impossible d'initialiser la librairie excel ! Code: " + std::to_string(hr));
if (FAILED(pApplication.CreateInstance("Excel.Application"))) throw std::runtime_error("CreateInstance failed ! (Office est pas installé?)");
pApplication->PutVisible(VARIANT_FALSE, 0);
Excel::_WorkbookPtr pBook = pApplication->Workbooks->Add(Excel::xlWorksheet);
if (pBook == nullptr) {
pApplication->Quit();
throw std::runtime_error("Impossible de créer le workbook !");
}
Excel::_WorksheetPtr pSheet = pApplication->ActiveSheet;
if (pSheet == nullptr) {
pBook->Close(VARIANT_FALSE);
pApplication->Quit();
throw std::runtime_error("Impossible de créer le worksheet !");
}
pSheet = pApplication->Worksheets->Add();
pSheet->Name = "Results";
pApplication->PutDisplayAlerts(LOCALE_USER_DEFAULT, VARIANT_FALSE);
try {
//always throws here
pBook->SaveAs("C:\\test.xls", vtMissing, vtMissing, vtMissing, false, false, Excel::XlSaveAsAccessMode::xlShared, false, false, vtMissing, vtMissing, vtMissing);
}
catch (_com_error& comErr) {
printComError(comErr);
}
pBook->Close();
pApplication->PutDisplayAlerts(LOCALE_USER_DEFAULT, VARIANT_TRUE);
pApplication->Quit();
CoUninitialize();
Function printComError():
static inline void printComError(const _com_error& e) {
_bstr_t bstrSource(e.Source());
_bstr_t bstrDescription(e.Description());
AllocConsole();
freopen("CONOUT$", "w", stdout);
freopen("CONOUT$", "w", stderr);
// Print Com errors.
std::cerr << "Error" << std::endl;
std::cerr << "\tCode = " << e.Error() << std::endl;
std::cerr << "\tCode meaning = " << e.ErrorMessage() << std::endl;
std::cerr << "\tSource = " << bstrSource << std::endl;
std::cerr << "\tDescription = " << bstrDescription << std::endl;
}
I've added a try/catch block and the function printComError(comErr) is always called with the following error:
Error
Code = -2146827284
Code meaning = 0975EB58
Source = Microsoft Excel
Description = Microsoft Excel ne peut accÚder au fichier ½áC:\CB7A7C80á╗. Plusieurs raisons sont possiblesá:
ò Le nom du fichier ou le chemin d'accÞs nÆexiste pas. ò Ce fichier
est actuellement utilisÚ par un autre programme. ò Le classeur que
vous essayez dÆenregistrer porte le mÛme nom quÆun classeur
actuellement ouvert.
wich means, in english:
Error
Code = -2146827284
Code meaning = 0975EB58
Source = Microsoft Excel
Description = Microsoft Excel can't access file ½áC:\CB7A7C80á╗. Many reasons are possibleá:
ò File name or path didn't exist. ò This file is in use by another application
ò The sheet that you want to save has the same name that another opened sheet
Apparently, error -2146827284 is like 0x800A03EC wich means NAME_NOT_FOUND, but its a newly created file, and didn't exist. The weird thing is that the error report that the filename is '½áC:\CB7A7C80á╗', but the first parameter of the SaveAs function is "c:\\test.xls".
I've seen related questions in Stackoverflow, but I didn't find useful answers.
Is this error related to an encoding of my string? Have you some hints to get the SaveAs function working?

This is just a rights/permission problem. If you use Excel itself as an end user, it will display a dialog box that say this
You don’t have permission to save in this location. Contact the
administrator to obtain permission. Would you like to save in the
Documents folder instead?
But, if you run Excel as admin, it will work fine, or it can work, because, at least, it works for me.
Note however that, programmatically, it indeed reports the error with a funny name like CB7A7C80 (it's a temp file it uses behind the scene) instead of the original one (the corruption is only at beginning and end of your string). I do reproduce that funny name also.

Related

how to handle excel files using C++?

I'm new to C++, And I want to enter values into an excel spreadsheet using C++, I know we can handle files using fstream but how to get a specific column or row using this method.
If you want to persist in using C++ (despite the comments above), this sample will give you an idea of the coding work needed to use C++ to automate the Excel application (as you might do in VBA or C#) rather than manipulate the file using a known file format (using a third-party library). The sample opens an existing worksheet in the background, adds 1 to the value in cell A1 on Sheet1, and then saves it.
Whether this is a suitable or efficient solution for your case will depend on what exactly you are trying to do with the files.
NB. Only works with the MS Visual Studio compiler.
The hard-coded paths to the import libraries may be different on your computer, and may depend on your Excel version.
//Import all the type libraries
#import "C:\Program Files\Microsoft Office\Root\VFS\ProgramFilesCommonX86\Microsoft Shared\OFFICE16\MSO.dll" \
rename("RGB","RGB_mso") rename("DocumentProperties","DocumentProperties_mso")
using namespace Office;
#import "C:\Program Files\Microsoft Office\root\vfs\ProgramFilesCommonX86\Microsoft Shared\VBA\VBA6\VBE6EXT.OLB"
using namespace VBIDE;
#import "C:\Program Files\Microsoft Office\root\Office16\EXCEL.EXE" \
rename( "DialogBox", "ExcelDialogBox" ) \
rename( "RGB", "ExcelRGB" ) \
rename( "CopyFile", "ExcelCopyFile" ) \
rename( "ReplaceText", "ExcelReplaceText" ) \
exclude( "IFont", "IPicture" )
#include <iostream>
using namespace std;
int main()
{
HRESULT hr = CoInitializeEx(0, COINIT_MULTITHREADED);
Excel::_ApplicationPtr pXL;
if (FAILED(pXL.CreateInstance("Excel.Application")))
{
cout << "Could not create instance of Excel" << endl;
return 1;
}
try
{
//Uncomment this to see what is going on during each step
//pXL->Visible = true;
Excel::_WorkbookPtr pWb = pXL->Workbooks->Open(L"c:\\temp\\testbook.xlsx");
//Gets number from cell A1 in Sheet1 and increments
Excel::_WorksheetPtr pSheet = pWb->Worksheets->Item[L"Sheet1"];
Excel::RangePtr pRng = pSheet->Cells;
_variant_t val = pRng->Item[1][1];
double dVal{ val };
pRng->Item[1][1] = ++dVal;
pWb->Save();
pWb->Close();
}
catch (_com_error ce)
{
cout << "Something went wrong" << endl;
_bstr_t bstrDesc = ce.Description();
if( ! bstrDesc )
{
cout << " Unknown Error" << endl;
}
else
{
cout << " Error text: " << bstrDesc << endl;
}
}
pXL->Quit();
}
EDIT: In answer to the unspoken question why is it Excel::_ApplicationPtr, Excel::_WorkbookPtr etc, but for a Range it is Excel::RangePtr (no _)? Absolutely no idea.
C++ is probably no the best choice to use if you want to manipulate .xlsx files. You would be better off either using macros in Excel using VBA, or you can write an Excel plugin using VSTO. If you really need to stick with C++, consider if you really need native Excel format. Maybe just .csv file be sufficient.
OpenXLSX is a great library for reading and writing excel sheets using C++. Its API documentation is not in the Github repo. It can be found here. The examples in the repo are quite extensive and detailed. Build instructions are also straightforward.
I have tried it on Linux and Windows (with MinGW). It works great and has the same behavior on both OSs.

FindResource call returns NULL for text file with type RCDATA

I'm writing a C++ program that needs to handle large chunks of data, specifically text files, however the data is longer than the given string limit. As a result I am attempting to load a text file as a resource file, however whenever I call FindResource the response is a null handle.
I have gone over other stack overflow posts, and MSDN to attempt to correctly implement this functionality however nothing I have tried has returned a valid handle.
C++ code:
HGLOBAL res_handle = NULL;
HRSRC res;
char * res_data;
DWORD res_size;
auto g_hInstance = GetModuleHandle(NULL);
res = FindResource(g_hInstance, MAKEINTRESOURCE(MY_RESOURCE), RT_RCDATA);
std::cout << res << std::endl;
std::cout << GetLastError() << std::endl;
res_handle = LoadResource(NULL, res);
std::cout << GetLastError() << std::endl;
res_data = (char*)LockResource(res_handle);
std::cout << GetLastError() << std::endl;
res_size = SizeofResource(NULL, res);
std::cout << GetLastError() << std::endl;
Output:
0000000000000000
1813
87
87
87
Resource.h:
//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ generated include file.
// Used by Resource.rc
//
#define MY_RESOURCE 300
// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE 103
#define _APS_NEXT_COMMAND_VALUE 40002
#define _APS_NEXT_CONTROL_VALUE 1001
#define _APS_NEXT_SYMED_VALUE 101
#endif
#endif
Resource.rc:
MY_RESOURCE RCDATA "C:\\Users\\foobar\\Desktop\\projects\\foobar\\test0.txt"
This works for me. I suspect something about your build setup is going wrong.
I started with an empty project in Visual Studio 2017 and the only thing I changed in the project settings was Configuration Properties > General > Character Set > "Use Unicode Character Set"
Source.cpp
#include <iostream>
#include <Windows.h>
#include "resource.h"
int main() {
auto hinst = ::GetModuleHandleW(NULL);
std::cout << hinst << std::endl;
auto hrsrc = ::FindResourceW(hinst, MAKEINTRESOURCE(IDTHING), RT_RCDATA);
std::cout << hrsrc << std::endl;
auto hglob = ::LoadResource(hinst, hrsrc);
std::cout << hglob << std::endl;
return 0;
}
resource.h
#define IDTHING 300
Resource.rc
#include "resource.h"
IDTHING RCDATA "C:\\Windows\\notepad.exe"
I completely avoided all the auto-generated resource stuff from VS and just wrote them as plain text files.
If you run in the debugger, stop after hglob is assigned and drop its value into a memory window, I can clearly see the PE header of notepad.exe.
When you receive error codes from GetLastError(), you should either call FormatMessage API (with FORMAT_MESSAGE_FROM_SYSTEM flag) or use error lookup tool, “Tools / Error Lookup” in visual studio.
Code 1813 translates into the following message: “The specified resource type cannot be found in the image file.”
As you see, it complains about resource type.
I don’t think you need RCDATA. It’s something very old from 16 bit windows. In Win32, custom resources types are strings, not integers. RCDATA is not even supported in the IDE.
The correct way — right click on your .rc file, “Add Resource”, “Import”, browse for the file you want to include, visual studio will ask you resource type, write any string e.g. text will do, then pass that string as the last parameter of FindResource function. If you're building with the default project settings, it's Unicode, so you'll need to pass Unicode string L"text" there.

C++ Encoding (special character Ø,é...)

I work with
C++/Windows/minGw
I get from file .xml a string with special character
The rise witting on the file xml is "Quimby_éé_ØØ R90 GP_NomPoints.txt"
The result is different with strangs characters
My file.xml sounds ok :
<?xml version="1.0" encoding="UTF-8"?>
Test :
When I get from file .txt a string with special character ,it dosesn't work
When I write the string to .txt file it works fine.
Then There might be some problem with the ide console.
My code:
void parser_fichier_xml(string fich,string &ActPoints,string &NomPoints)
{
//string ActPoints;
//string NomPoints;
TiXmlDocument doc(fich.c_str());
if(doc.LoadFile(TIXML_ENCODING_UTF8))
{
TiXmlHandle hdl(&doc);
TiXmlElement *elem = hdl.FirstChildElement("GeometryData").FirstChildElement("Element").Element(); //Création de elem (arbre DOM constituant noeud --enfant)
if(!elem)
{
cout<<"le noeud à atteindre n'existe pas"<<endl;
//return 1;
} //boucle pour vérifier que l'élément ait bien un enfant
/* ********* Recuperer chemin nompoint actpoint dans balise XML *********** */
ActPoints = elem->Attribute("ActPoints");
NomPoints = elem->Attribute("NomPoints");
/* *****test dans fichier de sorti ***** */
string const nomFichier("Z:/Production/Methodes/InfoTec/Developpement/Zeiss_PCM/toCALYPSO/test.txt");
ofstream fichier(nomFichier.c_str());
if(fichier)
{
fichier << NomPoints<< endl;
fichier.close();
}
else
cerr << "Impossible d'ouvrir le fichier test.txt !" << endl;
/* ************************************************** */
debug_string("Chemin ActPoints: ",ActPoints,"Chemin NomPoints: ",NomPoints); //affiche dans console
}
else
{
cerr << "Erreur d'ouverture du fichier .XML" << endl;
}
}
As answers, I doesn't like a function that replace special character but something that changes all
If someone may help me
Thks a lot
The windows console barely supports Unicode, definitely not UTF-8, and MinGW's std::cout doesn't help you. Sorry.
Thks for answers,
For comments.i will minimise my vode the next time.
I know what UFT-8 is ,
ASCII isn't enough (without Ø ,é ...)
UNICODE is theoric
For pratical , i've 2 choices ISO 8859 or UTF-8 and
UTF-8 is more complete (japanese ,etc..)
Test :
I tried this special chars " à " and the result is " á "
The display(cout or printf) is not important because i get the value(the result) like path.
i'm going to try with ISO 8859 but even with a file text that contains "éé_ØØ " , the console doesn't write same "éé_ØØ "
Sorry i can't send the picture of the result (this website ask 10 reputations)

Connecting to MS Access database using C++ using Visual Studio 2008

I need some serious help trying to connect to an Access database using VS 2008's C++. I have done this in C# but I cant figure this out in C++. I need to use C++ for the connection because I am grabbing data using pre-compiled C++ code. I would really appreciate some help with this. Thanks
I would like to odbc, but if you have another recommendation then I could change my mind.I am trying to connect to an Access database, the Northwind sample database, by following this example,
http://msdn.microsoft.com/en-us/library/cc811599.aspx
I am using a Windows 7 OS with Visual C++ 2008 for the compiler and IDE. The program is a console application. This example is specified for Access 2007 .accdb file types. Once I get it running correctly I will switch the path name, queries, and table names to my database. Below is the code that fails to build. I don't know what is causing this:
Includes-->
fstream
cmath
complex
iostream
iomanip
vector
limits
stdlib.h
stdio.h
time.h
fcntl.h
string.h
ctype.h
icrsint.h
using namespace std;
#import C:\\Program Files\\Common Files\\system\\ado\\msado15.dll rename("EOF",
"AdoNSEOF")
_bstr_t bstrConnect="Provider=Microsoft.ACE.OLEDB.12.0;Data "
"Source=C:\\Users\\lriley\\Documents\\Northwind 2007.mdb;";
HRESULT hr;
int main()
{
::CoInitialize(NULL);
const char* DAM = "ADO";
ADODB::_ConnectionPtr pConn("ADODB.Connection");
hr = pConn->Open(bstrConnect, "admin", "", ADODB::adConnectUnspecified);
if(SUCCEEDED(hr))
{
cout<<DAM<<": Successfully connected to database. Data source name:\n "
<<pConn->GetConnectionString()<<endl;
// Prepare SQL query
_bstr_t query = "SELECT Customers.[Company], Customers.[First Name] FROM "
"Customers;";
cout <<DAM<<": SQL query \n "<<query<<endl;
// Execute
ADODB::_RecordsetPtr pRS("ADODB.Recordset");
hr = pRS->Open(query,
_variant_t((IDispatch *) pConn, true),
ADODB::adOpenUnspecified,
ADODB::adLockUnspecified,
ADODB::adCmdText);
if(SUCCEEDED(hr))
{
cout<<DAM<<": Retrieve schema info for the given result set: "<< endl;
ADODB::Fields* pFields = NULL;
hr = pRS->get_Fields(&pFields);
if(SUCCEEDED(hr) && pFields && pFields->GetCount() > 0)
{
for(long nIndex=0; nIndex < pFields->GetCount(); nIndex++)
{
cout << " | "<<_bstr_t(pFields->GetItem(nIndex)->GetName());
}
cout << endl;
}
else
{
cout << DAM << ": Error: Number of fields in the " <<
"result is set to zero." << endl;
}
cout<<DAM<<": Fetch the actual data: " << endl;
int rowCount = 0;
while (!pRS->AdoNSEOF)
{
for(long nIndex=0; nIndex < pFields->GetCount(); nIndex++)
{
cout<<" | "<<_bstr_t(pFields->GetItem(nIndex)->GetValue());
}
cout<< endl;
pRS->MoveNext();
rowCount++;
}
cout<<DAM<<": Total Row Count: " << rowCount << endl;
}
pRS->Close();
pConn->Close();
cout<<DAM<<": Cleanup Done" << endl;
}
else
{
cout<<DAM<<" : Unable to connect to data source: "<<bstrConnect<<endl;
}
::CoUninitialize();
return 0;
}
I recieve the following error when I try to build it:
fatal error C1010: unexpected end of file while looking for precompiled header. Did you forget to add #include "stdafx.h" to your source?
c:\users\lriley\documents\visual studio 2008\projects\test12\test12\test12.cpp
Any help would be appreciated.
Thanks
Dante
Well, it's been a while, but you are going to need something like: http://msdn.microsoft.com/en-us/library/ms714562%28v=vs.85%29.aspx, look at SQLConnect..., a lot of variations to a theme, but the 2nd parameter is basically the path to your access db (*.mdb) file.
good luck.
You just have to give in file properties *. Cpp
-> Precompiled Header-No Precompiled Headers
Use precompiled header file - Stdafx.h xxxx

c++ reading excel file containing data and graph

I am writing a program to read a excel file using c++. i need only the two columns of the file and the excel file just dont contain fields...it also contains graphs. there are 10 columns and the two columns i need are the first and 7th. converting into CSV didnt work. Any suggestions plz.
Thank you.
You need to save the file as CSV and then read in the CSV file using fopen. You also need to start doing your homework on your own and not asking people on the internet for help.
Excel can be driven programatically through its COM interface. Here is some code:
#include <windows.h>
#include <iostream>
#import "C:\Program Files\Common Files\Microsoft Shared\OFFICE11\MSO.DLL" rename( "RGB", "MSORGB" ), rename( "DocumentProperties", "MSODocProps" )
#import "C:\Program Files\Common Files\Microsoft Shared\VBA\VBA6\VBE6EXT.OLB" rename ( "Application", "VBApplication" )
namespace Excel
{
using namespace Office;
using namespace VBIDE;
}
#import "C:\Program Files\Microsoft Office\OFFICE11\Excel.EXE" \
rename( "RGB", "RGB_" ), \
rename( "CopyFile", "CopyFile_" ), \
rename( "DialogBox", "DialogBox_" ), \
rename( "ReplaceText", "ReplaceText_" )
int main(int argc, char* argv[])
{
::CoInitialize( NULL );
try
{
Excel::_ApplicationPtr excel( "Excel.Application" );
Excel::WorkbooksPtr wbs = excel->Workbooks;
Excel::_WorkbookPtr wb = wbs->Open( _bstr_t( "c:\\temp\\test.xls" ) );
Excel::_WorksheetPtr sheet = wb->Sheets->GetItem( 1 ); // First sheet
Excel::RangePtr range = sheet->Cells->GetItem( 1, 1 ); // Row, column
_variant_t value = range->Value;
}
catch( _com_error& e )
{
std::cerr << "0x" << std::hex << e.Error() << std::endl;
}
catch( ... )
{
std::cerr << "Unexpected error" << std::endl;
}
::CoUninitialize();
return 0;
}
This code gets the value in cell A1 in a spreadsheet loaded from disc. You should be able to go from here to reading your two columns of data.