vc++操作excel

目的:通過vc++讀取和寫入excel

環境:vs2012 office2010

1.創建一個新的工程,選擇mfc application,選擇dialog,在advanced features選擇automation(這一項我不確定有沒有必要,查資料說要選上,我就選上了)

2.創建完工程後,在dialog對話框上右擊,選擇class wizard(或是ctrl+shift+x)

3.在add class下拉框內選擇add class from typelib

4.在add class from下面選擇file,然後你的office安裝目錄下的EXCEL.EXE(我的目錄是C:\Program Files (x86)\Microsoft Office\Office14\EXCEL.EXE)

5.在下面添加6個類到我們的工程,(_Application, Worksheets, _Worksheet, Workbooks, _Workbook, Range)注意有的有下劃線,別添加錯了

6.這個時候如果直接編譯程序會提示錯誤,大體錯誤的信息如下

Error	1	error C1083: Cannot open compiler generated file: 'd:\code\vc\exceltojson\exceltojson\debug\excel.tlh': Permission denied	d:\code\vc\exceltojson\exceltojson\capplication.h	3	1	EXCELToJSON


 

	2	IntelliSense: declaration modifiers are incompatible with previous declaration	d:\code\VC\EXCELToJSON\EXCELToJSON\Debug\excel.tlh	573	19	EXCELToJSON


出現很多錯誤,幾乎都與excel.tlh這個文件相關。

解決方法就是把導入的6個類對應的頭文件最開始的一句話

#import "C:\\Program Files (x86)\\Microsoft Office\\Office14\\EXCEL.EXE" no_namespace


 

註釋掉,也就是刪掉。具體原因不明,估計是這個已經更新不用了,但是模版裏面沒有改。所以引用了沒有的東西出的錯

7.做完上面的操作,再編譯一下,可能還有錯誤,錯誤提示如下:

Error	2	error C2059: syntax error : ','	d:\code\vc\exceltojson\exceltojson\crange.h	336	1	EXCELToJSON


 

	3	IntelliSense: expected a type specifier	d:\code\VC\EXCELToJSON\EXCELToJSON\CRange.h	336	10	EXCELToJSON


解決方法,把Range這個類自動生成的頭文件(我這是CRage.h)裏面的

VARIANT DialogBox()
	{
		VARIANT result;
		InvokeHelper(0xf5, DISPATCH_METHOD, VT_VARIANT, (void*)&result, NULL);
		return result;
	}


 

改成

VARIANT _DialogBox()
	{
		VARIANT result;
		InvokeHelper(0xf5, DISPATCH_METHOD, VT_VARIANT, (void*)&result, NULL);
		return result;
	}


 

也就是前面加一個下劃線。具體原因不明,估計是新版本後有同名的函數

(PS: 汗,話說ms也真夠坑爹,自己的各種不兼容)

8.這個時候編譯一下,估計沒什麼問題了,那麼我們就需要寫代碼操作excel了。網上找的代碼都差不多,不過裏面有幾句初始化com的

::CoInitialize(NULL);
	if(!AfxOleInit())
	{
		AfxMessageBox(L"cannot initialize com");
		return 0;
	}
	::CoUninitialize();


在新版本的vs上會引起這個錯誤

Debug Assertion Failed!

Program: D:\code\VC\EXCELToJSON\Debug\EXCELToJSON.exe

File: f:\dd\vctools\vc7libs\ship\atlmfc\src\mfc\oleinit.cpp

Line: 49

For information on how your program can cause an assertion failure, see the Visual C++ documentation on asserts.

(Press Retry to debug the application)

這個錯誤提示更是個坑,主要原因是你的com組件初始化了兩次。是由AfxOleInit這個函數導致的。(PS:你說我初始化兩次不行,直說不就行了,整得跟真事似的,我哪來的f盤。真是無語。再說,這麼常見的問題,爲什麼不能有一個準確的解釋)

解決方法就是把這一段初始化的程序刪掉,不用了。爲什麼?你可以全局匹配一下AfxOleInit,你會發現在app class內(就是和你的dlg class同名,僅僅差了Dlg的一個創建工程是系統自建的類,比如我的EXCELToJSONDlg.cpp,我們的代碼大部分都是寫到這裏面,然後會有一個EXCELToJSON.cpp類。就是在這個類裏面),系統自建的代碼有了這個初始化的程序:

// Initialize OLE libraries
	if (!AfxOleInit())
	{
		AfxMessageBox(IDP_OLE_INIT_FAILED);
		return FALSE;
	}


9.這樣應該沒什麼問題了,在你的程序內引入上面6個類的頭文件,然後就可以操作了。實例如下:

#include "stdafx.h"
#include "EXCELToJSON.h"
#include "EXCELToJSONDlg.h"
#include "DlgProxy.h"
#include "afxdialogex.h"
#include "CApplication.h"
#include "CRange.h"
#include "CWorkbook.h"
#include "CWorkbooks.h"
#include "CWorksheet.h"
#include "CWorksheets.h"
void CEXCELToJSONDlg::OnBnClickedButtonConvert()
{
	// TODO: Add your control notification handler code here
	CApplication app;
	CRange range;
	CWorkbook book;
	CWorkbooks books;
	CWorksheet sheet;
	CWorksheets sheets;
	LPDISPATCH lpdisp;
	COleVariant vresult;
	COleVariant covtrue((short)TRUE);
	COleVariant covfalse((short)FALSE);
	COleVariant covoptional((long)DISP_E_PARAMNOTFOUND, VT_ERROR);

	//create the server
	if(!app.CreateDispatch(L"Excel.Application"))
	{
		AfxMessageBox(L"Cannot start Excel server");
		return;
	}
	app.put_Visible(FALSE);
	books.AttachDispatch(app.get_Workbooks());

	//open file
	lpdisp = books.Open(
		ExcelFile,
		covoptional, covfalse, covoptional, covoptional, covoptional,
		covoptional, covoptional, covoptional, covoptional, covoptional,
		covoptional, covoptional, covoptional, covoptional);

	//get workbook
	book.AttachDispatch(lpdisp);
	
	//get worksheets
	sheets.AttachDispatch(book.get_Worksheets());

	lpdisp = book.get_ActiveSheet();
	sheet.AttachDispatch(lpdisp);

	//get the used range
	CRange usedrange;
	usedrange.AttachDispatch(sheet.get_UsedRange());

	//get used row
	range.AttachDispatch(usedrange.get_Rows());
	long irownum = range.get_Count();
	long istartrow = usedrange.get_Row();
	
	//get used column
	range.AttachDispatch(usedrange.get_Columns());
	long icolnum = range.get_Count();
	long istartcol = usedrange.get_Column();
	
	CString skey = L"";
	CString svalue = L"";
	for(int j=2; j<icolnum+1; j++)
	{
		JSON JSONObject;
		range.AttachDispatch(usedrange.get_Item(COleVariant((long)1), COleVariant((long)j)).pdispVal);
		vresult = range.get_Text();		
		JSONObject.LanguageTag = vresult.bstrVal;
		for(int i=2; i<irownum+1; i++)
		{
			range.AttachDispatch(usedrange.get_Item(COleVariant((long)i), COleVariant((long)1)).pdispVal);
			vresult = range.get_Text();
			skey = vresult.bstrVal;
			range.AttachDispatch(usedrange.get_Item(COleVariant((long)i), COleVariant((long)j)).pdispVal);
			vresult = range.get_Text();
			svalue = vresult.bstrVal;
			
		}
		
		
	}
	double dvalue = 3;
	usedrange.put_Item(COleVariant((long)2), COleVariant((long)2), COleVariant((double)dvalue));//write to excel
	book.Save();
	book.Close(covoptional, COleVariant(ExcelFile), covoptional);
	books.Close();
	app.Quit();

	
}


 

上面代碼是打開一個指定路徑的excel,然後獲得使用的sheet(也就是你最後一次保存退出的那個sheet,這個你也可以通過裏面的其他方法獲得excel裏面的其他sheet),然後獲得這個sheet內使用過的區域,並遍歷獲取其數據,後面一點是向指定的地方插入一個數據。在vs2012下,裏面的一些函數名字變了,setItem 改成put_Item等,你可以在提示的函數內找一下,看名字,應該能猜出來

 

xlsx文件其實是一個zip的壓縮包,你可以改其後綴名然後解壓,裏面都是一些xml文件和一些圖片等配置文件。如果你有時間,可以自己解碼excel文件。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章