Access to an excel file via C++

Pages: 1234
Thanks for writing this examples, but I still don't understand how you get the info from ole/com viewer and how you understand what the required parameters are... Can you please explain this? I think if I understand how it works, I'll be able to do things on my own.
If you are going to use the IDispatch interfaces rather than generate headers using the midl compiler, I suggest you wrap the calls to Invoke and GetIDsOfNames into wrapper functions.

AutowrapNameInvoke is the most useful, in that it allows you to insert only the optional named arguments that you are interested in.

Autowrap.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
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
#define COBJMACROS
#define CINTERFACE

#include <Windows.h>
#include <tchar.h>

#include <stdlib.h>

HRESULT AutowrapInvoke(int type, VARIANT * pvResult, void * pDispIn,
					   OLECHAR * szName, int cArgs, ...) {

	HRESULT			hr;

	DISPPARAMS		dispparamsAuto;
	EXCEPINFO		excepinfoAuto;
	UINT			uArgErrAuto;

	VARIANT			* pvAuto = NULL;

	DISPID			dispidSave = DISPID_PROPERTYPUT;
	DISPID			dispidAuto;

	va_list			marker;

	IDispatch		* pDisp;

	if( !pDisp )
		return E_INVALIDARG;

	hr = IUnknown_QueryInterface((IUnknown *) pDispIn, &IID_IDispatch, &pDisp);
	if(!SUCCEEDED(hr))
		goto CLEANUP;

	if(cArgs > 0) {

		va_start(marker, cArgs);

		pvAuto = (VARIANT *) malloc(sizeof(VARIANT) * cArgs);

		if(!pvAuto) {
				
			hr = E_OUTOFMEMORY;

			goto CLEANUP;

		}

	}

	{
		VARIANT		* pvar = NULL;
		int			i;

		for(i = 0; i < cArgs; ++i) {			

			pvar = va_arg(marker, VARIANT *);

			memcpy((pvAuto + i), pvar, sizeof(VARIANT));

		}
	
		hr = IDispatch_GetIDsOfNames(pDisp, &IID_NULL, &szName, 1,
			LOCALE_SYSTEM_DEFAULT, &dispidAuto);

	}

	if(FAILED(hr)) {

		pvResult = NULL;

		goto CLEANUP;

	}

	dispparamsAuto.cArgs = cArgs;
	dispparamsAuto.cNamedArgs = 0;
	dispparamsAuto.rgvarg = pvAuto;
	dispparamsAuto.rgdispidNamedArgs = NULL;

	if(type & DISPATCH_PROPERTYPUT) {

		dispparamsAuto.cNamedArgs = 1;
		dispparamsAuto.rgdispidNamedArgs = &dispidSave;

	}

	hr = IDispatch_Invoke(pDisp, dispidAuto, &IID_NULL,
		LOCALE_SYSTEM_DEFAULT, type, &dispparamsAuto, pvResult,
		&excepinfoAuto, &uArgErrAuto);

	va_end(marker);

CLEANUP:

	if(pvAuto)	free(pvAuto);

	if(pDisp)		IDispatch_Release(pDisp);

	return hr;
}

HRESULT AutowrapNamedInvoke(VARIANT * pvResult, void * pDispIn,
					   OLECHAR * szName, int cArgs, ...) {

	int				i;

	HRESULT			hr;

	DISPPARAMS		dispparamsAuto = {0};
	EXCEPINFO		excepinfoAuto;
	UINT			uArgErrAuto;

	VARIANT			* pvAuto = NULL;

	DISPID			* pdispidAuto;
	LPOLESTR		* szNames;

	IDispatch		* pDisp;

	va_list			marker;

	if( !pDispIn )
		return E_INVALIDARG;

	hr = IUnknown_QueryInterface((IUnknown *) pDispIn, &IID_IDispatch, &pDisp);
	if(!SUCCEEDED(hr))
		goto CLEANUP;

	va_start(marker, cArgs);

	pdispidAuto = (DISPID *) malloc(sizeof(DISPID) * (1 + cArgs));
	szNames = (LPOLESTR *) malloc(sizeof(LPOLESTR) * (1 + cArgs));
	if(cArgs > 0)
		pvAuto = (VARIANT *) malloc(sizeof(VARIANT) * cArgs);

	if(!pdispidAuto || !szNames || (!pvAuto && cArgs > 0)) {
				
		hr = E_OUTOFMEMORY;

		goto CLEANUP;

	}

	szNames[0] = szName;

	for(i = 0; i < cArgs; ++i) {
			
		VARIANT		* pvar;

		szNames[i + 1] = va_arg(marker, OLECHAR *);

		pvar = va_arg(marker, VARIANT *);
		memcpy((pvAuto + i), pvar, sizeof(VARIANT));

	}

	hr = IDispatch_GetIDsOfNames(pDisp, &IID_NULL, szNames, cArgs + 1,
		LOCALE_SYSTEM_DEFAULT, pdispidAuto);

	if(FAILED(hr)) {

		pvResult = NULL;

		goto CLEANUP;

	}

	dispparamsAuto.cArgs = cArgs;
	dispparamsAuto.cNamedArgs = cArgs;
	if(cArgs > 0) {
		dispparamsAuto.rgvarg = pvAuto;
		dispparamsAuto.rgdispidNamedArgs = &pdispidAuto[1];
	}

	hr = IDispatch_Invoke(pDisp, pdispidAuto[0], &IID_NULL,
		LOCALE_SYSTEM_DEFAULT, DISPATCH_METHOD, &dispparamsAuto, pvResult,
		&excepinfoAuto, &uArgErrAuto);

	va_end(marker);

CLEANUP:

	if(pdispidAuto)	free(pdispidAuto);
	if(szNames)		free(szNames);
	if(pvAuto)		free(pvAuto);

	if(pDisp)		IDispatch_Release(pDisp);

	return hr;
}

HRESULT GetDispProperty(void * pdisp, OLECHAR * szName, VARIANT * pvResult) {

	return AutowrapInvoke(DISPATCH_PROPERTYGET, 
		pvResult, pdisp, szName, 0);

}

HRESULT PutDispProperty(void * pdisp, OLECHAR * szName,
						VARIANT * pvValue, VARIANT * pvResult) {

	return AutowrapInvoke(DISPATCH_PROPERTYPUT,
		pvResult, pdisp, szName, 1, pvValue);
}
Thanks for trying to help the original poster and myself too CBurn! I see this is your first post here and a good one too!

I'm hoping actually someone else can step in here and help the original poster because I'm afraid I have to bow out. Fact is I'm stuck bad, and I simply can't justify spending more time on this at this point. Where I fell down and am stuck, is at the final stage necessary to have a complete working system, and that is the need to be able to set the Worksheet within an opened Workbook. I have successfully been able to open Excel using CoCreateInstance, set it to visible, and read/write data to whatever cell desired. But the last needed step is to be able to Select() the desired Worksheet within a Workbook, and if one can't do that, then all the rest is useless I'm afraid.

Also, my intent was to look at wrapping all the GetIDsOfNames()/Invoke() calls once I fully understood it. I expect if you wrote those wrappers or whever did so fully understands it.

Also, for me coding is a hobby as well as work, so I try to do everything myself rather than relying on code others have provided, although I do study other's code carefully to learn at times - as I'll likely study yours above. I have not done so yet at this point, and I'm wondering if it would succeed where mine is failing.

Actually, I even tried two radically different techniques for accomplishing this. Excel is a funny beast. I have been told that only IDispatch based calls work on Excel, but the fact is that isn't entirely true. It is possible to make direct VTable calls on some things. The big problem there is all of them don't succeed. Only some do. When I was having difficulty selecting the desired Worksheet using IDispatch I pulled out my direct access code, which relies on a typelib dumper program I wrote several years ago which dumps a type lib and creates C++ headers for whatever type lib I choose. I thought perhaps I could succeed there where I couldn't with IDispatch. However, I failed there too.

Here is a PowerBASIC program (I posted it earlier, but here I attached comments as to what is going on) that shows what I can't seem to achieve in 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
#Compile Exe
#Dim All

Function PBMain() As Long
  Local pDisp, pWorkBook, pWorkSheet As IDispatch
  Local vVnt1,vVnt2 As Variant

  pDisp = NewCom "Excel.Application"                         ' Calls CoCreateInstance() And Returns IDispatch*
  vVnt1=1                                                    ' Sets vVnt = TRUE For SetVisible() Call
  Object Let pDisp.Visible=vVnt1                             ' Makes Runnine Excel App Visible
  vVnt1="C:\Code\PwrBasic\PBCC6\XLAuto\Book1.xls"            ' Puts *.xls FileName In BSTR in VARIANT vVnt1
  Object Call pDisp.WorkBooks.Open(vVnt1) To pWorkBook       ' Execute WorkBooks::Open() Through IDispatch::Invoke()
  vVnt1="Sheet1"                                             ' Puts "Sheet1" In BSTR 
  Object Call pWorkbook.Sheets(vVnt1).Select                 ' Here's The Hard One I Don't Know How To Do In C++!!!!!!!!!!!
  Object Get pWorkbook.ActiveSheet To vVnt1                  ' Get Worksheet Object Pointer into vVnt1
  Set pWorkSheet = vVnt1                                     ' Set It To Another IDispatch Variable pWorkSheet
  vVnt1="A1"
  Object Get pWorkSheet.Range(vVnt1).Value To vVnt2
  Con.Print Variant$(vVnt2)
  Con.WaitKey$
  Object Call pWorkbook.Close

  PBMain=0
End Function 


This is the line I can't do to save my life ...

Object Call pWorkbook.Sheets(vVnt1).Select

In that line VARIANT vVnt1 is holding the BSTR for the sheet desired. For direct VTable access here are the two C++ interfaces auto generated by previously mentioned dump utility for the Sheets and the IWorksheets interfaces ...

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
interface Sheets : IDispatch
{
 virtual HRESULT __stdcall GetApplication(IDispatch** RHS)=0;
 virtual HRESULT __stdcall GetCreator(XlCreator* RHS)=0;
 virtual HRESULT __stdcall GetParent(IDispatch** RHS)=0;
 virtual HRESULT __stdcall Add(VARIANT Before, VARIANT After, VARIANT Count, VARIANT Type, LONG lcid, IDispatch** RHS)=0;
 virtual HRESULT __stdcall Copy(VARIANT Before, VARIANT After, LONG lcid)=0;
 virtual HRESULT __stdcall GetCount(LONG* RHS)=0;
 virtual HRESULT __stdcall Delete(LONG lcid)=0;
 virtual HRESULT __stdcall FillAcrossSheets(Range* Range, XlFillWith Type, LONG lcid)=0;
 virtual HRESULT __stdcall GetItem(VARIANT Index, IDispatch** RHS)=0;
 virtual HRESULT __stdcall Move(VARIANT Before, VARIANT After, LONG lcid)=0;
 virtual HRESULT __stdcall Get_NewEnum(IUnknown** RHS)=0;
 virtual HRESULT __stdcall _PrintOut(VARIANT From, VARIANT To, VARIANT Copies, VARIANT Preview, VARIANT ActivePrinter, VARIANT PrintToFile, VARIANT Collate, LONG lcid)=0;
 virtual HRESULT __stdcall PrintPreview(VARIANT EnableChanges, LONG lcid)=0;
 virtual HRESULT __stdcall Select(VARIANT Replace, LONG lcid)=0;
 virtual HRESULT __stdcall GetHPageBreaks(HPageBreaks** RHS)=0;
 virtual HRESULT __stdcall GetVPageBreaks(VPageBreaks** RHS)=0;
 virtual HRESULT __stdcall GetVisible(LONG lcid, VARIANT* RHS)=0;
 virtual HRESULT __stdcall SetVisible(LONG lcid, VARIANT RHS)=0;
 virtual HRESULT __stdcall Get_Default(VARIANT Index, IDispatch** RHS)=0;
 virtual HRESULT __stdcall PrintOut(VARIANT From, VARIANT To, VARIANT Copies, VARIANT Preview, VARIANT ActivePrinter, VARIANT PrintToFile, VARIANT Collate, VARIANT PrToFileName, LONG lcid)=0;
};


interface IWorksheets : IDispatch
{
 virtual HRESULT __stdcall GetApplication(IDispatch** RHS)=0;
 virtual HRESULT __stdcall GetCreator(XlCreator* RHS)=0;
 virtual HRESULT __stdcall GetParent(IDispatch** RHS)=0;
 virtual HRESULT __stdcall Add(VARIANT Before, VARIANT After, VARIANT Count, VARIANT Type, LONG lcid, IDispatch** RHS)=0;
 virtual HRESULT __stdcall Copy(VARIANT Before, VARIANT After, LONG lcid)=0;
 virtual HRESULT __stdcall GetCount(LONG* RHS)=0;
 virtual HRESULT __stdcall Delete(LONG lcid)=0;
 virtual HRESULT __stdcall FillAcrossSheets(Range* Range, XlFillWith Type, LONG lcid)=0;
 virtual HRESULT __stdcall GetItem(VARIANT Index, IDispatch** RHS)=0;
 virtual HRESULT __stdcall Move(VARIANT Before, VARIANT After, LONG lcid)=0;
 virtual HRESULT __stdcall Get_NewEnum(IUnknown** RHS)=0;
 virtual HRESULT __stdcall _PrintOut(VARIANT From, VARIANT To, VARIANT Copies, VARIANT Preview, VARIANT ActivePrinter, VARIANT PrintToFile, VARIANT Collate, LONG lcid)=0;
 virtual HRESULT __stdcall PrintPreview(VARIANT EnableChanges, LONG lcid)=0;
 virtual HRESULT __stdcall Select(VARIANT Replace, LONG lcid)=0;
 virtual HRESULT __stdcall GetHPageBreaks(HPageBreaks** RHS)=0;
 virtual HRESULT __stdcall GetVPageBreaks(VPageBreaks** RHS)=0;
 virtual HRESULT __stdcall GetVisible(LONG lcid, VARIANT* RHS)=0;
 virtual HRESULT __stdcall SetVisible(LONG lcid, VARIANT RHS)=0;
 virtual HRESULT __stdcall Get_Default(VARIANT Index, IDispatch** RHS)=0;
 virtual HRESULT __stdcall PrintOut(VARIANT From, VARIANT To, VARIANT Copies, VARIANT Preview, VARIANT ActivePrinter, VARIANT PrintToFile, VARIANT Collate, VARIANT PrToFileName, LONG lcid)=0;
};


Using direct VTable calls I can successfully obtain a pointer to either of these interfaces, which I believe are listed in the *.idl file a duals, but I can't successfully pull off the Select() calls that would cause my desired Worksheet to become the ActiveWorksheet. Lot f angst here!

Here is my direct vtable access code and the console output with it, that shows beyond any doubt that direct vtable access (early binding) is possible with Excel ...

(continued)



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
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
/*
Output

Successfully Obtained _Application*!
ptrXLApp->SetVisible(true) Succeeded!
pWorkbooks = 8768828
pWorkbooks->Open() Succeeded!
pWorkBook = 8769548
pWorkBook->QueryInterface(IID_Workbook,(void**)&pBook) Succeeded!
pBook      = 8769548

Got pSheets!
pSheets = 8769836
pSheets->Select() Failed!
*/

#define  UNICODE
#define  _UNICODE
#include <windows.h>
#include <tchar.h>
#include <cstdio>
#include "IOffice.h"
#include "IExcel.h"
const CLSID CLSID_XLApplication = {0x00024500,0x0000,0x0000,{0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x46}};
const IID   IID_Application     = {0x000208D5,0x0000,0x0000,{0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x46}};
const IID   IID_Workbook        = {0x000208DA,0x0000,0x0000,{0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x46}};

int main()
{
 IDispatch*      pDispWorkbook=NULL;
 _Application*   ptrXLApp=NULL;
 Workbooks*      pWorkbooks=NULL;
 IDispatch*      pSheet=NULL;
 //_Worksheet*     pWkSht;
 IDispatch*      pWorkBook=NULL;
 _Workbook*      pBook=NULL;
 VARIANT         vBool;
 LCID            lcid;
 HRESULT         hr;

 CoInitialize(NULL);
 lcid=GetUserDefaultLCID();
 hr=CoCreateInstance(CLSID_XLApplication,NULL,CLSCTX_LOCAL_SERVER,IID_Application,(void**)&ptrXLApp);
 if(SUCCEEDED(hr))
 {
    printf("Successfully Obtained _Application*!\n");
    vBool.vt=VT_BOOL, vBool.boolVal=TRUE;
    hr=ptrXLApp->SetVisible(LOCALE_USER_DEFAULT,TRUE);
    if(SUCCEEDED(hr))
    {
       printf("ptrXLApp->SetVisible(true) Succeeded!\n");
       hr=ptrXLApp->GetWorkbooks(&pWorkbooks);
       if(SUCCEEDED(hr))
       {
          VARIANT vNull;
          vNull.vt=VT_NULL;
          printf("pWorkbooks = %u\n",(unsigned)pWorkbooks);
          BSTR strFileName=SysAllocString(L"C:\\Tallies\\Documents\\13200211.xls");
          //hr=pWorkbooks->Add(vNull,LOCALE_USER_DEFAULT,&pDispWorkbook);
          //virtual HRESULT __stdcall Add(VARIANT Template, LONG lcid, IDispatch** RHS)=0;
          //virtual HRESULT __stdcall Open(BSTR Filename, LONG lcid, IDispatch** RHS)=0;
          hr=pWorkbooks->Open(strFileName,vNull,vNull,vNull,vNull,vNull,vNull,vNull,vNull,vNull,vNull,vNull,vNull,LOCALE_USER_DEFAULT,(IDispatch**)&pWorkBook);
          if(SUCCEEDED(hr))
          {
             printf("pWorkbooks->Open() Succeeded!\n");
             printf("pWorkBook = %d\n",pWorkBook);
             // 4051  interface _Worksheet : IDispatch
             hr=pWorkBook->QueryInterface(IID_Workbook,(void**)&pBook);
             if(SUCCEEDED(hr))
             {
                printf("pWorkBook->QueryInterface(IID_Workbook,(void**)&pBook) Succeeded!\n");
                printf("pBook      = %d\n",(int)pBook);
                getchar();

                //interface _Workbook : IDispatch
                //{
                // ...
                // virtual HRESULT __stdcall GetSheets(Sheets** RHS)=0;
                // virtual HRESULT __stdcall GetWorksheets(Sheets** RHS)=0;
                // ...
                //}

                //interface _Worksheet : IDispatch
                //{
                //  virtual HRESULT __stdcall Select(VARIANT Replace, LONG lcid)=0;
                //}

                //interface _application
                //{
                // virtual HRESULT __stdcall GetSheets(Sheets** RHS)=0;   // 3082
                //}

                /*
                Sheets* pSheets=NULL;
                hr=pBook->GetWorksheets(&pSheets);
                if(SUCCEEDED(hr))
                {
                   printf("Got pSheets!\n");
                   printf("pSheets = %d\n",(int)pSheets);
                   getchar();
                   BSTR strSheet=SysAllocString(L"FMT-5");
                   //virtual HRESULT __stdcall Select(VARIANT Replace, LONG lcid)=0;
                   VARIANT vSheet;
                   vSheet.vt=VT_BSTR;
                   vSheet.bstrVal=strSheet;
                   hr=pSheets->Select(vSheet,lcid);
                   if(SUCCEEDED(hr))
                   {
                      printf("pSheets->Select() Succeeded!\n");
                   }
                   else
                   {
                      printf("pSheets->Select() Failed!\n");
                   }
                   getchar();
                   pSheets->Release();
                }
                else
                {
                 printf("Didn't Get pSheets!  I'm Done!\n");
                 printf("pSheets = %d\n",(int)pSheets);
                }
                */

                Sheets* pSheets=NULL;
                hr=ptrXLApp->GetSheets(&pSheets);
                if(SUCCEEDED(hr))
                {
                   printf("Got pSheets!\n");
                   printf("pSheets = %d\n",(int)pSheets);
                   DISPID dispidNamed           =  DISPID_PROPERTYPUT;  //0x000000eb;
                   VARIANT vResult;
                   VariantInit(&vResult);
                   VARIANT CallArgs[1];                                        //  Invoke Workbooks::Open(&Workbook)  << returns IDispatch** of Workbook Object
                   CallArgs[0].vt               = VT_BSTR;
                   CallArgs[0].bstrVal          = SysAllocString(L"FMT-5");
                   DISPPARAMS DispParams;
                   DispParams.rgvarg            = CallArgs;
                   //dispidNamed=DISPID_PROPERTYPUT;
                   DispParams.rgdispidNamedArgs = &dispidNamed;
                   DispParams.cArgs             = 1;
                   DispParams.cNamedArgs        = 0;
                   //hr=pXLBook->Invoke(dispid,IID_NULL,LOCALE_USER_DEFAULT,DISPATCH_PROPERTYGET,&NoArgs,&vResult,NULL,NULL);
                   hr=ptrXLApp->Invoke(0x000000eb,IID_NULL,LOCALE_USER_DEFAULT,DISPATCH_PROPERTYPUT,&DispParams,&vResult,NULL,NULL);
                   if(SUCCEEDED(hr))
                   {
                      printf("pSheets->Select() Succeeded!\n");
                   }
                   else
                   {
                      printf("pSheets->Select() Failed!\n");
                   }




                   //BSTR strSheet=SysAllocString(L"FMT-5");
                   //virtual HRESULT __stdcall Select(VARIANT Replace, LONG lcid)=0;
                   //VARIANT vSheet;
                   //vSheet.vt=VT_BSTR;
                   //vSheet.bstrVal=strSheet;
                   //hr=pSheets->Select(vSheet,lcid);
                   //if(SUCCEEDED(hr))
                   //{
                   //   printf("pSheets->Select() Succeeded!\n");
                   //}
                   //else
                   //{
                   //   printf("pSheets->Select() Failed!\n");
                   //}
                   getchar();
                   pSheets->Release();
                }
                else
                {
                   printf("Couldn't Get pSheets!\n");
                }

                pBook->Release();
             }
             else
             {
                printf("pWkSht->QueryInterface(IID_Workbook,(void**)&pBook) Failed!\n");
             }
             getchar();
             pWorkBook->Release();
          }
          else
          {
             printf("pWorkbooks->Open() Failed!\n");
          }
          getchar();
          pWorkbooks->Release();
       }
    }
    getchar();
    ptrXLApp->Quit();
    ptrXLApp->Release();
 }
 CoUninitialize();
 getchar();

 return 0;
}
In the include files above are IOffice.h and IExcel.h which were auto-generated by my TypeLib dumper code. I wouldn't be able to post them here because they are rather massive.

Just doing pure IDispatch using dispinterfaces, I fail too, and that is because I don't fully understand how to use the DISPPARAMS struct, and otherwise set up the IDispatch::Invoke() call to makle happen what I want, which is to Select() my desired Worksheet. Here's my failing code ...

CODE]
Output

CoCreateInstance(Excel) Succeeded!
pXLApp.Visible = true Succeeded!
_Application::Workbooks() Succeeded! - Got Workbooks**
Got ID For 'Open' In GetIDsOfNames()
Succeeded In pXLBooks->Open(Book1.xls)!
Got Sheets dispid!
dispid = 485
Failed To Get pXLSheets!
dispid (Value) = 6
vResult.bstrVal = Hello, World!
*/
[/CODE]
Last edited on
It won't fit! Posting it wouldn't do any good though it doesn't work anyway. Still stuck at not being able to execute a successful Select() call.

So the original poster has several options which I'll list...

1) Realize that he is attempting to accomplish one of the hardest thing one could ever attempt to do in C++ and give up;

2) Get someone else to provide wrapper code that works and instruct him how to use it. Perhaps UBurn's code above is it, or perhaps such code auto-generated by Microsoft's ATL is the answer;

3) Buy a software package/library for several hundred bucks that provides easy wrapper functionality. I believe the OP already has a free version of something like that;

4) Use another programming language with built in fuinctionality such as my PowerBASIC code shown above. That does it in all of about 20 lines of code. PowerBASIC's older versions (which would run the above supplied code) sell for about $90. The most recent versions are about $200;

For myself, I'll have to pick this up at some future date and try again maybe. Sorry I can't take any more time on this. I tried!
Freddie, about the select method on the sheet object, Excel is expecting a Boolean value in the Replace argument. You seem to be supplying a BSTR.

Try:
1
2
3
VARIANT vSheet;
vSheet.vt = VT_BOOL;
vSheet.boolVal = VARIANT_TRUE; /* or VARIANT_FALSE */


http://msdn.microsoft.com/en-us/library/ff841056(v=office.15).aspx

Using the wrapper functions I posted above, I rewrote your last code block as shown below. My call to the Select function returns S_OK.

exceltest.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
#define COBJMACROS
#define MOVE_DISP_RESULT(pvarResult, toPTR)	toPTR = (pvarResult)->pdispVal;

int wmain(int argc, WCHAR * argv[]) {

	HRESULT		hr;
	CLSID		clsid;
	VARIANT		varResult;

	hr = OleInitialize(NULL);

	hr = CLSIDFromProgID(L"Excel.Application", &clsid);

	IDispatch	* pApp;
	hr = CoCreateInstance(&clsid, NULL, CLSCTX_LOCAL_SERVER, &IID_IDispatch, &pApp);

	VARIANT varVisible; varVisible.vt = VT_BOOL; varVisible.boolVal = VARIANT_TRUE;
	hr = PutDispProperty(pApp, L"Visible", &varVisible, &varResult);

	IDispatch * pWorkbooks;
	hr = GetDispProperty(pApp, L"Workbooks", &varResult);
	MOVE_DISP_RESULT(&varResult, pWorkbooks);

	IDispatch * pWorkbook;
	VARIANT varFilename; 
	varFilename.vt = VT_BSTR; 
varFilename.bstrVal = SysAllocString(L"C:\\Tallies\\Documents\\13200211.xls");
	hr = AutowrapNamedInvoke(&varResult, pWorkbooks, L"Open", 1,
		L"FileName", &varFilename);
	MOVE_DISP_RESULT(&varResult, pWorkbook);

	IDispatch * pWorksheets;
	hr = GetDispProperty(pWorkbook, L"Worksheets", &varResult);
	MOVE_DISP_RESULT(&varResult, pWorksheets);

	VARIANT varReplace; varReplace.vt = VT_BOOL; varReplace.boolVal = VARIANT_TRUE;
	hr = AutowrapNamedInvoke(&varResult, pWorksheets, L"Select", 1,
		L"Replace", &varReplace);

	IDispatch_Release(pWorksheets);
	IDispatch_Release(pWorkbook);
	IDispatch_Release(pWorkbooks);
	hr = AutowrapNamedInvoke(&varResult, pApp, L"Quit", 0);
	IDispatch_Release(pApp);

	return 0;
}
Last edited on
Hey CBurn! Thanks for helping with this! I got your program running with C++ in Mingw by just putting (void**) casts on the [out] parameters of the various QueryInterface() type calls.

But note that the issue is to feed a BSTR containing the desired sheet name within the workbook so that Excel makes that sheet the sheet from which it will read/write data. Its going to take feeding it a BSTR and not a bool to do that. Here is my modification of your code that just uses a Workbook which everyone should have, i.e., Book1.xls, which further has sheet1, sheet2, sheet3, etc. You'll need to fill in your BSTR with where you have it on your computer, but for me I put it in ...

C:\\Code\\CodeBlks\\XL_Dispatch\\Book1.xls

Your program opens Excel and makes it visible, loads the Book1.xls file, and sheet1 is selected. How to select sheet2?????

some of the formatting will likely not be right...
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
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
#define COBJMACROS
#define CINTERFACE
#include <Windows.h>
#include <cstdio>
#include <tchar.h>
#include <stdlib.h>
#define MOVE_DISP_RESULT(pvarResult, toPTR)	toPTR = (pvarResult)->pdispVal;


HRESULT AutowrapInvoke(int type, VARIANT * pvResult, void * pDispIn,
					   OLECHAR * szName, int cArgs, ...) {

	HRESULT			hr;

	DISPPARAMS		dispparamsAuto;
	EXCEPINFO		excepinfoAuto;
	UINT			uArgErrAuto;

	VARIANT			* pvAuto = NULL;

	DISPID			dispidSave = DISPID_PROPERTYPUT;
	DISPID			dispidAuto;

	va_list			marker;

	IDispatch		* pDisp;

	if( !pDisp )
		return E_INVALIDARG;

	hr = IUnknown_QueryInterface((IUnknown *) pDispIn, &IID_IDispatch, (void**)&pDisp);
	if(!SUCCEEDED(hr))
		goto CLEANUP;

	if(cArgs > 0) {

		va_start(marker, cArgs);

		pvAuto = (VARIANT *) malloc(sizeof(VARIANT) * cArgs);

		if(!pvAuto) {

			hr = E_OUTOFMEMORY;

			goto CLEANUP;

		}

	}

	{
		VARIANT		* pvar = NULL;
		int			i;

		for(i = 0; i < cArgs; ++i) {

			pvar = va_arg(marker, VARIANT *);

			memcpy((pvAuto + i), pvar, sizeof(VARIANT));

		}

		hr = IDispatch_GetIDsOfNames(pDisp, &IID_NULL, &szName, 1,
			LOCALE_SYSTEM_DEFAULT, &dispidAuto);

	}

	if(FAILED(hr)) {

		pvResult = NULL;

		goto CLEANUP;

	}

	dispparamsAuto.cArgs = cArgs;
	dispparamsAuto.cNamedArgs = 0;
	dispparamsAuto.rgvarg = pvAuto;
	dispparamsAuto.rgdispidNamedArgs = NULL;

	if(type & DISPATCH_PROPERTYPUT) {

		dispparamsAuto.cNamedArgs = 1;
		dispparamsAuto.rgdispidNamedArgs = &dispidSave;

	}

	hr = IDispatch_Invoke(pDisp, dispidAuto, &IID_NULL,
		LOCALE_SYSTEM_DEFAULT, type, &dispparamsAuto, pvResult,
		&excepinfoAuto, &uArgErrAuto);

	va_end(marker);

CLEANUP:

	if(pvAuto)	free(pvAuto);

	if(pDisp)		IDispatch_Release(pDisp);

	return hr;
}

HRESULT AutowrapNamedInvoke(VARIANT * pvResult, void * pDispIn,
					   OLECHAR * szName, int cArgs, ...) {

	int				i;

	HRESULT			hr;

	DISPPARAMS		dispparamsAuto = {0};
	EXCEPINFO		excepinfoAuto;
	UINT			uArgErrAuto;

	VARIANT			* pvAuto = NULL;

	DISPID			* pdispidAuto;
	LPOLESTR		* szNames;

	IDispatch		* pDisp;

	va_list			marker;

	if( !pDispIn )
		return E_INVALIDARG;

	hr = IUnknown_QueryInterface((IUnknown *) pDispIn, &IID_IDispatch, (void**)&pDisp);
	if(!SUCCEEDED(hr))
		goto CLEANUP;

	va_start(marker, cArgs);

	pdispidAuto = (DISPID *) malloc(sizeof(DISPID) * (1 + cArgs));
	szNames = (LPOLESTR *) malloc(sizeof(LPOLESTR) * (1 + cArgs));
	if(cArgs > 0)
		pvAuto = (VARIANT *) malloc(sizeof(VARIANT) * cArgs);

	if(!pdispidAuto || !szNames || (!pvAuto && cArgs > 0)) {

		hr = E_OUTOFMEMORY;

		goto CLEANUP;

	}

	szNames[0] = szName;

	for(i = 0; i < cArgs; ++i) {

		VARIANT		* pvar;

		szNames[i + 1] = va_arg(marker, OLECHAR *);

		pvar = va_arg(marker, VARIANT *);
		memcpy((pvAuto + i), pvar, sizeof(VARIANT));

	}

	hr = IDispatch_GetIDsOfNames(pDisp, &IID_NULL, szNames, cArgs + 1,
		LOCALE_SYSTEM_DEFAULT, pdispidAuto);

	if(FAILED(hr)) {

		pvResult = NULL;

		goto CLEANUP;

	}

	dispparamsAuto.cArgs = cArgs;
	dispparamsAuto.cNamedArgs = cArgs;
	if(cArgs > 0) {
		dispparamsAuto.rgvarg = pvAuto;
		dispparamsAuto.rgdispidNamedArgs = &pdispidAuto[1];
	}

	hr = IDispatch_Invoke(pDisp, pdispidAuto[0], &IID_NULL,
		LOCALE_SYSTEM_DEFAULT, DISPATCH_METHOD, &dispparamsAuto, pvResult,
		&excepinfoAuto, &uArgErrAuto);

	va_end(marker);

CLEANUP:

	if(pdispidAuto)	free(pdispidAuto);
	if(szNames)		free(szNames);
	if(pvAuto)		free(pvAuto);

	if(pDisp)		IDispatch_Release(pDisp);

	return hr;
}

HRESULT GetDispProperty(void * pdisp, OLECHAR * szName, VARIANT * pvResult)
{
	return AutowrapInvoke(DISPATCH_PROPERTYGET, pvResult, pdisp, szName, 0);
}

HRESULT PutDispProperty(void * pdisp, OLECHAR * szName,	VARIANT * pvValue, VARIANT * pvResult)
{
	return AutowrapInvoke(DISPATCH_PROPERTYPUT,pvResult, pdisp, szName, 1, pvValue);
}

int main(int argc, WCHAR * argv[])
{
	HRESULT		hr;
	CLSID		clsid;
	VARIANT		varResult;

	hr = OleInitialize(NULL);

	hr = CLSIDFromProgID(L"Excel.Application", &clsid);

	IDispatch	* pApp;
	hr = CoCreateInstance(&clsid, NULL, CLSCTX_LOCAL_SERVER, &IID_IDispatch, (void**)&pApp);

	VARIANT varVisible; varVisible.vt = VT_BOOL; varVisible.boolVal = VARIANT_TRUE;
	hr = PutDispProperty(pApp, L"Visible", &varVisible, &varResult);

	IDispatch * pWorkbooks;
	hr = GetDispProperty(pApp, L"Workbooks", &varResult);
	MOVE_DISP_RESULT(&varResult, pWorkbooks);

	IDispatch * pWorkbook;
	VARIANT varFilename;
	varFilename.vt = VT_BSTR;
          //varFilename.bstrVal = SysAllocString(L"C:\\Tallies\\Documents\\13200211.xls");
          varFilename.bstrVal = SysAllocString(L"C:\\Code\\CodeBlks\\XL_Dispatch\\Book1.xls");
	hr = AutowrapNamedInvoke(&varResult, pWorkbooks, L"Open", 1,	L"FileName", &varFilename);
	MOVE_DISP_RESULT(&varResult, pWorkbook);

	IDispatch * pWorksheets;
	hr = GetDispProperty(pWorkbook, L"Worksheets", &varResult);
	MOVE_DISP_RESULT(&varResult, pWorksheets);

	VARIANT varReplace; varReplace.vt = VT_BOOL; varReplace.boolVal = VARIANT_TRUE;
	hr = AutowrapNamedInvoke(&varResult, pWorksheets, L"Select", 1,	L"Replace", &varReplace);
          getchar();

	IDispatch_Release(pWorksheets);
	IDispatch_Release(pWorkbook);
	IDispatch_Release(pWorkbooks);
	hr = AutowrapNamedInvoke(&varResult, pApp, L"Quit", 0);
	IDispatch_Release(pApp);

	return 0;
}

I finally got mine crunched down to fit within 8192 bytes by removing useful whitespace and other useful stuff ...

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
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
#include <windows.h>
#include <tchar.h>
#include <cstdio>
const CLSID CLSID_XLApplication     = {0x00024500,0x0000,0x0000,{0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x46}};
const IID   IID_Application         = {0x000208D5,0x0000,0x0000,{0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x46}};

int main()
{
 DISPPARAMS        NoArgs={NULL,NULL,0,0};
 IDispatch*        pXLApp=NULL;
 IDispatch*        pXLBooks=NULL;
 IDispatch*        pXLBook=NULL;
 DISPPARAMS        DispParams;
 VARIANT           CallArgs[1];
 VARIANT           vResult;
 DISPID            dispid;
 HRESULT           hr;
 LCID              lcid;

 CoInitialize(NULL);
 hr=CoCreateInstance(CLSID_XLApplication, NULL, CLSCTX_LOCAL_SERVER, IID_Application, (void**)&pXLApp);
 if(SUCCEEDED(hr))
 {
    OLECHAR* szVisible=(OLECHAR*)L"Visible";
    lcid=GetUserDefaultLCID();
    hr=pXLApp->GetIDsOfNames(IID_NULL,&szVisible,1,lcid,&dispid);
    if(SUCCEEDED(hr))
    {
       VariantInit(&CallArgs[0]);
       CallArgs[0].vt=VT_BOOL;
       CallArgs[0].boolVal=TRUE;
       DISPID dispidNamed=DISPID_PROPERTYPUT;
       DispParams.rgvarg=CallArgs;
       DispParams.rgdispidNamedArgs=&dispidNamed;
       DispParams.cArgs=1;
       DispParams.cNamedArgs=1;
       VariantInit(&vResult);
       hr=pXLApp->Invoke(dispid,IID_NULL,LOCALE_USER_DEFAULT,DISPATCH_PROPERTYPUT,&DispParams,&vResult,NULL,NULL);
       if(SUCCEEDED(hr))
       {
          OLECHAR* szWorkbooks=(OLECHAR*)L"Workbooks";
          hr=pXLApp->GetIDsOfNames(IID_NULL,&szWorkbooks,1,GetUserDefaultLCID(),&dispid);
          if(SUCCEEDED(hr))
          {
             VariantInit(&vResult);
             hr=pXLApp->Invoke(dispid,IID_NULL,LOCALE_USER_DEFAULT,DISPATCH_PROPERTYGET,&NoArgs,&vResult,NULL,NULL);
             if(SUCCEEDED(hr))
             {
                pXLBooks=vResult.pdispVal;
                OLECHAR* szOpen=(OLECHAR*)L"Open";
                hr=pXLBooks->GetIDsOfNames(IID_NULL,&szOpen,1,GetUserDefaultLCID(),&dispid);
                if(SUCCEEDED(hr))
                {
                   VariantInit(&vResult);
                   CallArgs[0].vt               = VT_BSTR;
                   CallArgs[0].bstrVal          = SysAllocString(L"C:\\Code\\CodeBlks\\XL_Dispatch\\Book1.xls");
                   DispParams.rgvarg            = CallArgs;
                   DispParams.rgdispidNamedArgs = &dispidNamed;
                   DispParams.cArgs             = 1;
                   DispParams.cNamedArgs        = 0;
                   hr=pXLBooks->Invoke(dispid,IID_NULL,LOCALE_USER_DEFAULT,DISPATCH_METHOD|DISPATCH_METHOD,&DispParams,&vResult,NULL,NULL);
                   if(SUCCEEDED(hr))
                   {
                      pXLBook=vResult.pdispVal;
                      IDispatch* pXLSheets=NULL;
                      OLECHAR* szSheets=(OLECHAR*)L"Sheets";
                      hr=pXLApp->GetIDsOfNames(IID_NULL,&szSheets,1,GetUserDefaultLCID(),&dispid);
                      if(SUCCEEDED(hr))
                      {
                         VariantClear(&vResult);
                         VariantInit(&vResult);
                         CallArgs[0].vt=VT_BSTR;
                         CallArgs[0].bstrVal=SysAllocString(L"Sheet2");
                         DispParams.rgvarg=CallArgs;
                         dispidNamed=DISPID_PROPERTYPUT;
                         DispParams.rgdispidNamedArgs = &dispidNamed;
                         DispParams.cArgs=1;
                         DispParams.cNamedArgs=0;
                         hr=pXLBook->Invoke(dispid,IID_NULL,LOCALE_USER_DEFAULT,DISPATCH_METHOD,&DispParams,&vResult,NULL,NULL);
                         if(SUCCEEDED(hr))
                         {
                            printf("You Won't See This Because The Call To Select sheet2 Will Fail!\n");
                            pXLSheets=vResult.pdispVal;
                            pXLSheets->Release();
                         }
                         OLECHAR* szActiveSheet=(OLECHAR*)L"ActiveSheet";
                         hr=pXLApp->GetIDsOfNames(IID_NULL,&szActiveSheet,1,GetUserDefaultLCID(),&dispid);
                         if(SUCCEEDED(hr))
                         {
                            IDispatch* pXLSheet=NULL;
                            VariantInit(&vResult);
                            hr=pXLApp->Invoke(dispid,IID_NULL,LOCALE_USER_DEFAULT,DISPATCH_PROPERTYGET,&NoArgs,&vResult,NULL,NULL);
                            if(SUCCEEDED(hr))
                            {
                               pXLSheet=vResult.pdispVal;
                               OLECHAR* szRange=(OLECHAR*)L"Range";
                               hr=pXLSheet->GetIDsOfNames(IID_NULL,&szRange,1,GetUserDefaultLCID(),&dispid);
                               if(SUCCEEDED(hr))
                               {
                                  IDispatch* pXLRange=NULL;
                                  VariantInit(&vResult);
                                  CallArgs[0].vt=VT_BSTR,
                                  CallArgs[0].bstrVal=SysAllocString(L"A1");
                                  DispParams.rgvarg=CallArgs;
                                  DispParams.rgdispidNamedArgs=0;
                                  DispParams.cArgs=1;
                                  DispParams.cNamedArgs=0;
                                  hr=pXLSheet->Invoke(dispid,IID_NULL,LOCALE_USER_DEFAULT,DISPATCH_PROPERTYGET,&DispParams,&vResult,NULL,NULL);
                                  if(SUCCEEDED(hr))
                                  {
                                     pXLRange=vResult.pdispVal;
                                     OLECHAR* szValue=(OLECHAR*)L"Value";
                                     hr=pXLRange->GetIDsOfNames(IID_NULL,&szValue,1,GetUserDefaultLCID(),&dispid);
                                     if(SUCCEEDED(hr))
                                     {
                                        VariantClear(&CallArgs[0]);
                                        hr=pXLRange->Invoke(dispid,IID_NULL,LOCALE_USER_DEFAULT,DISPATCH_PROPERTYGET,&NoArgs,&vResult,NULL,NULL);
                                        if(SUCCEEDED(hr))
                                        {
                                           wprintf(L"vResult.bstrVal = %s\n",vResult.bstrVal);
                                           VariantClear(&vResult);
                                        }
                                        pXLRange->Release();
                                     }
                                  }
                               }
                               pXLSheet->Release();
                            }
                         }
                      }
                      pXLBook->Release();
                   }
                }
                pXLBooks->Release();
             }
          }
       }
       getchar();
    }
    VariantInit(&vResult);  // Try to do _Application::Close()
    hr=pXLApp->Invoke(0x0000012e,IID_NULL,LOCALE_USER_DEFAULT,DISPATCH_METHOD,&NoArgs,&vResult,NULL,NULL);
    pXLApp->Release();
 }
 CoUninitialize();

 return 0;
}
Last edited on
There's some crazy stuff around the middle where I'm trying to set up the call to make "sheet2" active, and I'm just throwing everything at it I can think of to try and get it to work. One does that of course when one doesn't know what one is doing and one is desperate! :)

On the Book1.xls file I'm using sheet1 just has "Hello, World!" on it, which the program above satisfactorily outputs to the console, but on sheet2 in A1 I put something or other which I'm trying to get output. If you put something on sheet2 and get it output, then you know you've figured it out!!!

Last edited on
Freddie, if you want to use a BSTR to select a specific sheet, the Worksheets collection Item method will take either a BSTR (the name of the sheet) or a LONG (index of the sheet in the sheets collection). The Item method returns the requested Worksheet object, which also has a Select method.

Add the following to my post above:
1
2
3
4
5
6
7
8
9
10
11
12
13
	IDispatch * pWorksheet;
	VARIANT varSheetname;
	varSheetname.vt = VT_BSTR;
	varSheetname.bstrVal = SysAllocString(L"Sheet2"); /* or whatever the name of your sheet is. */
	hr = AutowrapInvoke(DISPATCH_PROPERTYGET, &varResult, pWorksheets, L"Item", 1,
		&varSheetname);
	MOVE_DISP_RESULT(&varResult, pWorksheet);

	VARIANT varReplace; varReplace.vt = VT_BOOL; varReplace.boolVal = VARIANT_TRUE;
	hr = AutowrapNamedInvoke(&varResult, pWorksheet, L"Select", 1,
		L"Replace", &varReplace);

	IDispatch_Release(pWorksheet);


Note: When you are invoking the Item method on a collection, you need to use AutowrapInvoke (not AutowrapNamedInvoke) and specify DISPATCH_PROPERTYGET as the type parameter. AutowrapNamedInvoke forces DISPATCH_METHOD to be passed to Invoke.
Last edited on
CBurn, I think that might be it! I'm all coded out for today, but tomorrow I'll try it. Thanks!
Say CBurn, I don't think this is right here at the top of your AutowrapInvoke() function ...

1
2
3
4
5
6
7
8
9
10
IDispatch* pDisp;

if(!pDisp )
    return E_INVALIDARG;

Should that be ...

[CODE]
if(!pDispIn)
   return E_INVALIDARG;


The way I fixed it is the way you have it in AutowrapNamedInvoke(). Unless I'm not understanding it, my guess as to why it works OK with the error is that there is always some random bytes other than NULLs at dangling uninitialized pointer pDisp?

Fred

[/CODE]
I've been trying to follow the examples you posted and try to do stuff on my own, but no progress. :(

Freddie, in one of your posts, you've included "IOffice.h" and "IExcel.h". How did you generate those files?

I've tried the option in Ole/Com Viewer to SaveAs .h file but it didn't generate anything. The only thing that it generated was a file called EXCEL.IDL.

Alin
I haven't forgotten you Cojones, as this is still your thread. I've kind of taken it over I guess though. Really, we are both trying to figure this out. In spite of the fact I ought to be working on other things today, I'm going to work on this 'till I get it, hopefully soon.

But to answer your questions.

Various versions of Office or Excel deal with the type library in different ways. On this computer Excel is telling me its Excel 2010, but the actual exe is in a folder Office14, for whatever reason. If I open the version of OleView to which I have a shortcut on my Desktop (I may have various versions of that file too), and go to File >> View TypeLib, then navigate to my Office14 folder where Excel.exe is located, then select that file, OleView will run a lot of code to recreate the original Excel.idl file (IDL = Interface Definition Language). That is a good file for you to use as a start in understanding all this.

However, on other older versions of Excel, to get the *.idl file, you may need to select some other file such as an *.tlb, *.odl, or perhaps even something else.

But it sounds to me like you finally succeeded in getting a *.idl file. That is good. You are on the right track.
I have the same version of Office installed. And yes, I've used the OleView from there and I have generated the excel.idl file. It seems there's all the stuff that goes around in excel in there.

I was asking about how you generated the excel.h files because I've seen the code there and it seems a little more self explanatory. An example below:

hr=pWorkbooks->Open(strFileName,vNull,vNull,vNull,vNull,vNull,vNull,vNull,vNull,vNull,vNull,vNull,vNull,LOCALE_USER_DEFAULT,(IDispatch**)&pWorkBook);
Last edited on
Now, you can't include that *.idl code in your C++ code. It looks something like C or C++ but it isn't. The reason it isn't is because COM (Microsoft's Component Object Model) was designed to be language agnostic, and if they would have decided to use C or C++ for the language used to define COM interfaces, then COM would not have been useful, for example, to Visual Basic developers, who were really the main target of all this. Not the only target by any means - but the main one.

So the way it works is Idl is sort of a language agnostic way of defining interfaces, and Microsoft created a special compiler known as midl.exe whose main purpose was to create a binary type library file out the *.idl fed into it. That file generated from the *.idl is a *.tlb (type library) file. These can be stand alone or using the resource compiler they can be added to the exe.

The end result of all this is that Microsoft created a whole massive Api for reading type libraries and there is a whole lot of functions, interfaces, and complexity in that. However, it allowed non C or C++ languages to be able understand the interfaces if they can read this type library.

Now, what C or C++ coders want is simply a *.h file that allows them access to the interface definitions so that they can call the methods of the object.

Those two files you mentioned are just that, and I spent many months writing my own code using the type library Apis to generate them from a type library. Actually, my intent was to create something useful to me as a C++ coder like I have in my PowerBASIC coding where this is all very, very, easy. What happens there in PowerBASIC is there is a utility called their 'COM Browser'. Its a GUI utility that lists in a massive listview control all the type libraries on a system. One simply double clicks on a library - such as the Excel library, and the utility auto-generates the massive include file that PowerBASIC uses, and by including that in one's program, one has access to all the interfaces and methods. Its a lot like OleView, except that instead of producing the *.idl file, it produces PowerBASIC headers.

Now, if you have Visual Studio, Microsoft included a utility to do that for C++ coders as part of its ATL modules. I forget the name of it, but that was what kbw was alluding to when he said there was an easy way to do this. For my part though, I never liked it or ATL. That's why my intent was to produce my own utility to generate C or C++ headers exactly like I like to see from my PowerBASIC work.
I'll give you an example of what a PowerBASIC include looks like generated by their COM Browser. Because Excel has massive functionality and a lot of interfaces and methods, the whole file is about 700K. But it starts out like so ...

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
' Generated by: PowerBASIC COM Browser v.2.00.0086
' Date & Time : 4/5/2011 at 12:26 PM
' Options:
' Always use an Interface Prefix : Off
' Interface Prefix               : XL
' Prefix ProgIDs, ClassIDs...    : On
' Use ANSI Strings               : Off
' Use Singular Enumerations      : Off
' Generate Dispatch Interfaces   : On
' Include Parameter Names        : On
' Use Property Get/Set statements: On
'
' Library Name: Excel
' Library File: C:\Program Files\Microsoft Office\Office12\EXCEL.EXE
' Description : Microsoft Excel 12.0 Object Library
' Help File : C:\Program Files\Microsoft Office\Office12\VBAXL10.CHM
' Help Context : 0
' GUID : {00020813-0000-0000-C000-000000000046}
' LCID : 0
' Version : 1.6

' Version Dependent ProgIDs
$PROGID_Excel_Application12 = "Excel.Application.12"
$PROGID_Excel_Chart8 = "Excel.Chart.8"
$PROGID_Excel_Worksheet8 = "Excel.Sheet.8"

' Version Independent ProgIDs
$PROGID_Excel_Application = "Excel.Application"
$PROGID_Excel_Chart = "Excel.Chart"
$PROGID_Excel_Worksheet = "Excel.Sheet"


And here would be what an interface definition looks like, and it is such that allows one to call into it ...

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
' Interface Name  : Sheets
' This Interface cannot be created directly it can only
' be returned by a Method or Property in this library. 
Interface IDBind Sheets
  Member Get Application <148> () As IDispatch
  Member Get Creator <149> () As Long
  Member Get Parent <150> () As IDispatch
  Member Call Add <181> (Opt In Before As Variant<0>, Opt In After As Variant<1>, Opt In Count As Variant<2>, Opt In PB_Type As Variant<3>) As IDispatch
  Member Call Copy <551> (Opt In Before As Variant<0>, Opt In After As Variant<1>)
  Member Get Count <118> () As Long
  Member Call Delete <117> ()
  Member Call FillAcrossSheets <469> (In PB_Range As Range<0>, Opt In PB_Type As Long<1>)
  Member Get Item <170> (In Index As Variant<0>) As IDispatch
  Member Call Move <637> (Opt In Before As Variant<0>, Opt In After As Variant<1>)
  Member Get PropGet__NewEnum <-4> () As IUnknown
  Member Call Meth___PrintOut <905> (Opt In PB_From As Variant<0>, Opt In PB_To As Variant<1>, Opt In Copies As Variant<2>, Opt In Preview As Variant<3>, Opt In ActivePrinter As Variant<4>, Opt In PrintToFile As Variant<5>, Opt In PB_Collate _
    As Variant<6>)
  Member Call PrintPreview <281> (Opt In EnableChanges As Variant<0>)
  Member Call Select <235> (Opt In PB_Replace As Variant<0>)
  Member Get HPageBreaks <1418> () As HPageBreaks
  Member Get VPageBreaks <1419> () As VPageBreaks
  Member Get Visible <558> () As Variant
  Member Let Visible <558> (In RHS As Variant<1>)
  Member Get PropGet__Default <0> (In Index As Variant<0>) As IDispatch
  Member Call Meth__PrintOut <1772> (Opt In PB_From As Variant<0>, Opt In PB_To As Variant<1>, Opt In Copies As Variant<2>, Opt In Preview As Variant<3>, Opt In ActivePrinter As Variant<4>, Opt In PrintToFile As Variant<5>, Opt In PB_Collate _
    As Variant<6>, Opt In PrToFileName As Variant<7>)
  Member Call PrintOut <2361> (Opt In PB_From As Variant<0>, Opt In PB_To As Variant<1>, Opt In Copies As Variant<2>, Opt In Preview As Variant<3>, Opt In ActivePrinter As Variant<4>, Opt In PrintToFile As Variant<5>, Opt In PB_Collate As _
    Variant<6>, Opt In PrToFileName As Variant<7>, Opt In IgnorePrintAreas As Variant<8>)
End Interface 


Note the Select member above. That's what I'm fighting with!!!

Anyway, there is a lot more to all this. See those numbers above in angle brackets? They are dispatch IDs. Very important!

But backing up, those files you asked about were generated by code I have that attempts to produce for me (C++ headers) what the PowerBASIC COM Browser above is doing for PowerBASIC.

However, there's a whole lot more to all this, believe me.

Taking a very high level view, there are two general ways of using COM objects. One way - and the best way - and the easiest way - for C++ coders, or PowerBASIC coders, is direct VTable access. This is extremely fast. Its as fast as a function call. With direct VTable access, the object returns a pointer to a VTable - that is what an interface is, it is a block of memory containing function pointers. And the client can make direct method calls off that interface pointer.

But not all languages are able to do this. Things like VB Script, Jasva Script, so on and so forth, can't handle function pointer calls several levels of indirection removed. So what Microsoft did was create a whole 'nother Api for such languages lacking horsepower. It was called 'OLE Automation' and worked off something they cooked up called IDispatch.

The way IDispatch works is that there are only 7 methods in the interface. But two are very important. ::GetIDsOfNames returns the dispatch id (remember those) of a string name passed into it, which represents the name of the method one wants tro call. That is the dispatch ids you see in the PowerBASIC include above(they are also seen in hex in the *.idl). Then, once you have that dispid, the ::Invoke method of IDispatch calls the method within the object that you want to use. Invoke is incredibly ugly, as you can see. Just read up on IDispatch::Invoke in MSDN, and try to track down all the related structs such as DISPPARAMS, etc. Last word in mean!

Well, if its so mean and I'm using a powerful language such as C++ why not use direct VTable access instead of IDispatch which is so nasty? Well, the sorry fact is that Microsoft botched up the Excel COM infrastructure code and only IDispatch access works completely.

The whole reason I was trying VTable access was that I have full mastery of that whereas I'm weak at IDispatch. I hate it. And some of the direct VTable calls work and some don't. But if I were you I'd forget it because I don't think we can get it working with direct VTable access.

Hope this helps!
I got CBurn11's code working with his update Cojones!!! I want to thank him for that. If there is a new Member of the Month here he gets my vote!

So we're in! Now that we can programatically select the Worksheet within the Workbook we want, there's no stoping us.

The only thing that's bad for me is I don't understand CBurn11's code. I need to disect it to death to figure out how it works. But it does work. Here is the code of his that works with only one correction and some reformatting to suite my formatting tastes (sorry CBurn!). It works on basically the default workbook Excel creates, i.e., Book1.xls. And in that workbook are Sheets Sheet12, Sheet2, and Sheet3. You need to fix the path to wherever you have something like that. First I'll post CBurn's original C code version, then one I took the liberty of converting to C++ calls

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
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
// C Code
#define COBJMACROS
#define CINTERFACE
#include <Windows.h>
#include <cstdio>
#include <tchar.h>
#include <stdlib.h>
#define MOVE_DISP_RESULT(pvarResult, toPTR) toPTR = (pvarResult)->pdispVal;


HRESULT AutowrapInvoke(int type, VARIANT* pvResult, void* pDispIn, OLECHAR* szName, int cArgs, ...)
{
 HRESULT      hr;
 DISPPARAMS   dispparamsAuto;
 EXCEPINFO    excepinfoAuto;
 UINT         uArgErrAuto;
 VARIANT*     pvAuto = NULL;
 DISPID       dispidSave = DISPID_PROPERTYPUT;
 DISPID       dispidAuto;
 va_list      marker;
 IDispatch*   pDisp;

 if(!pDispIn)
  return E_INVALIDARG;
 hr = IUnknown_QueryInterface((IUnknown *) pDispIn, &IID_IDispatch, (void**)&pDisp);
 if(!SUCCEEDED(hr))
    goto CLEANUP;

 if(cArgs > 0)
 {
    va_start(marker, cArgs);
    pvAuto = (VARIANT *) malloc(sizeof(VARIANT) * cArgs);
    if(!pvAuto)
    {
       hr = E_OUTOFMEMORY;
       goto CLEANUP;
    }
 }
 {
  VARIANT  * pvar = NULL;
  int   i;

  for(i = 0; i < cArgs; ++i)
  {
      pvar = va_arg(marker, VARIANT *);
      memcpy((pvAuto + i), pvar, sizeof(VARIANT));
  }
  hr = IDispatch_GetIDsOfNames(pDisp, &IID_NULL, &szName, 1, LOCALE_SYSTEM_DEFAULT, &dispidAuto);
 }
 if(FAILED(hr))
 {
    pvResult = NULL;
    goto CLEANUP;
 }
 dispparamsAuto.cArgs = cArgs;
 dispparamsAuto.cNamedArgs = 0;
 dispparamsAuto.rgvarg = pvAuto;
 dispparamsAuto.rgdispidNamedArgs = NULL;
 if(type & DISPATCH_PROPERTYPUT)
 {
    dispparamsAuto.cNamedArgs = 1;
    dispparamsAuto.rgdispidNamedArgs = &dispidSave;
 }
 hr = IDispatch_Invoke(pDisp, dispidAuto, &IID_NULL, LOCALE_SYSTEM_DEFAULT, type, &dispparamsAuto, pvResult, &excepinfoAuto, &uArgErrAuto);
 va_end(marker);

 CLEANUP:
 if(pvAuto)
    free(pvAuto);
 if(pDisp)
    IDispatch_Release(pDisp);

 return hr;
}


HRESULT AutowrapNamedInvoke(VARIANT* pvResult, void* pDispIn, OLECHAR* szName, int cArgs, ...)
{
 int          i;
 HRESULT      hr;
 DISPPARAMS   dispparamsAuto = {0};
 EXCEPINFO    excepinfoAuto;
 UINT         uArgErrAuto;
 VARIANT*     pvAuto = NULL;
 DISPID*      pdispidAuto;
 LPOLESTR*    szNames;
 IDispatch*   pDisp;
 va_list      marker;

 if(!pDispIn)
    return E_INVALIDARG;
 hr = IUnknown_QueryInterface((IUnknown *) pDispIn, &IID_IDispatch, (void**)&pDisp);
 if(!SUCCEEDED(hr))
    goto CLEANUP;
 va_start(marker, cArgs);
 pdispidAuto = (DISPID *) malloc(sizeof(DISPID) * (1 + cArgs));
 szNames = (LPOLESTR *) malloc(sizeof(LPOLESTR) * (1 + cArgs));
 if(cArgs > 0)
    pvAuto = (VARIANT *) malloc(sizeof(VARIANT) * cArgs);
 if(!pdispidAuto || !szNames || (!pvAuto && cArgs > 0))
 {
    hr = E_OUTOFMEMORY;
    goto CLEANUP;
 }
 szNames[0] = szName;
 for(i = 0; i < cArgs; ++i)
 {
     VARIANT* pvar;
     szNames[i + 1] = va_arg(marker, OLECHAR*);
     pvar = va_arg(marker, VARIANT*);
     memcpy((pvAuto + i), pvar, sizeof(VARIANT));
 }
 hr = IDispatch_GetIDsOfNames(pDisp, &IID_NULL, szNames, cArgs + 1, LOCALE_SYSTEM_DEFAULT, pdispidAuto);
 if(FAILED(hr))
 {
    pvResult = NULL;
    goto CLEANUP;
 }
 dispparamsAuto.cArgs = cArgs;
 dispparamsAuto.cNamedArgs = cArgs;
 if(cArgs > 0)
 {
    dispparamsAuto.rgvarg = pvAuto;
    dispparamsAuto.rgdispidNamedArgs = &pdispidAuto[1];
 }
 hr = IDispatch_Invoke(pDisp, pdispidAuto[0], &IID_NULL, LOCALE_SYSTEM_DEFAULT, DISPATCH_METHOD, &dispparamsAuto, pvResult, &excepinfoAuto, &uArgErrAuto);
 va_end(marker);

 CLEANUP:
 if(pdispidAuto)
    free(pdispidAuto);
 if(szNames)
    free(szNames);
 if(pvAuto)
    free(pvAuto);
 if(pDisp)
    IDispatch_Release(pDisp);

 return hr;
}


HRESULT GetDispProperty(void * pdisp, OLECHAR * szName, VARIANT * pvResult)
{
 return AutowrapInvoke(DISPATCH_PROPERTYGET, pvResult, pdisp, szName, 0);
}


HRESULT PutDispProperty(void * pdisp, OLECHAR * szName, VARIANT * pvValue, VARIANT * pvResult)
{
 return AutowrapInvoke(DISPATCH_PROPERTYPUT,pvResult, pdisp, szName, 1, pvValue);
}


int main()
{
 HRESULT  hr;
 CLSID  clsid;
 VARIANT  varResult;

 hr = OleInitialize(NULL);
 hr = CLSIDFromProgID(L"Excel.Application", &clsid);
 IDispatch * pApp;
 hr = CoCreateInstance(&clsid, NULL, CLSCTX_LOCAL_SERVER, &IID_IDispatch, (void**)&pApp);

 VARIANT varVisible; varVisible.vt = VT_BOOL; varVisible.boolVal = VARIANT_TRUE;
 hr = PutDispProperty(pApp, L"Visible", &varVisible, &varResult);

 IDispatch * pWorkbooks;
 hr = GetDispProperty(pApp, L"Workbooks", &varResult);
 MOVE_DISP_RESULT(&varResult, pWorkbooks);

 IDispatch * pWorkbook;
 VARIANT varFilename;
 varFilename.vt = VT_BSTR;
 varFilename.bstrVal =
 SysAllocString
 (
   L"C:\\Code\\CodeBlks\\XL_Dispatch\\Book1.xls" // <<< need to specify path to file!!!
 );
 hr = AutowrapNamedInvoke(&varResult, pWorkbooks, L"Open", 1, L"FileName", &varFilename);
 MOVE_DISP_RESULT(&varResult, pWorkbook);

 IDispatch * pWorksheets;
 hr = GetDispProperty(pWorkbook, L"Worksheets", &varResult);
 MOVE_DISP_RESULT(&varResult, pWorksheets);

 // New Added From CBurn
 IDispatch * pWorksheet;
 VARIANT varSheetname;
 varSheetname.vt = VT_BSTR;
 varSheetname.bstrVal = SysAllocString(L"Sheet2");
 hr=AutowrapInvoke(DISPATCH_PROPERTYGET, &varResult, pWorksheets, L"Item", 1, &varSheetname);
 MOVE_DISP_RESULT(&varResult, pWorksheet);
 // End New Added From CBurn

 VARIANT varReplace; varReplace.vt = VT_BOOL; varReplace.boolVal = VARIANT_TRUE;
 hr = AutowrapNamedInvoke(&varResult, pWorksheet, L"Select", 1, L"Replace", &varReplace);
 getchar();

 IDispatch_Release(pWorksheet);
 IDispatch_Release(pWorksheets);
 IDispatch_Release(pWorkbook);
 IDispatch_Release(pWorkbooks);
 hr = AutowrapNamedInvoke(&varResult, pApp, L"Quit", 0);
 IDispatch_Release(pApp);

 return 0;
}
And here is the above code converted to C++ isms. Hope you don't mind CBurn11!!! Personally, I have nothing whatsoever against C code. I do it mostly myself. I'm actually stuck in some kind of strange reality between the two. I only compile as C++ but use absolutely nothing in the C++ Std. Library. I have my own String class, my own template based multi-dimensional array implementation, ODBC class, etc. But that's another story. But here's the above in 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
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
#include <Windows.h>
#include <cstdio>
#include <tchar.h>
#include <stdlib.h>
#define MOVE_DISP_RESULT(pvarResult, toPTR) toPTR = (pvarResult)->pdispVal;


HRESULT AutowrapInvoke(int type, VARIANT* pvResult, IDispatch* pDispIn, OLECHAR* szName, int cArgs, ...)
{
 HRESULT      hr;
 DISPPARAMS   dispparamsAuto;
 EXCEPINFO    excepinfoAuto;
 UINT         uArgErrAuto;
 VARIANT*     pvAuto = NULL;
 DISPID       dispidSave = DISPID_PROPERTYPUT;
 DISPID       dispidAuto;
 va_list      marker;
 IDispatch*   pDisp;

 if(!pDispIn)
    return E_INVALIDARG;
 hr = pDispIn->QueryInterface(IID_IDispatch, (void**)&pDisp);
 if(!SUCCEEDED(hr))
    goto CLEANUP;
 if(cArgs > 0)
 {
    va_start(marker, cArgs);
    pvAuto = (VARIANT *) malloc(sizeof(VARIANT) * cArgs);
    if(!pvAuto)
    {
       hr = E_OUTOFMEMORY;
       goto CLEANUP;
    }
 }
 {
  VARIANT  * pvar = NULL;
  int   i;

  for(i = 0; i < cArgs; ++i)
  {
      pvar = va_arg(marker, VARIANT *);
      memcpy((pvAuto + i), pvar, sizeof(VARIANT));
  }
  hr = pDisp->GetIDsOfNames(IID_NULL, &szName, 1, LOCALE_SYSTEM_DEFAULT, &dispidAuto);
 }
 if(FAILED(hr))
 {
    pvResult = NULL;
    goto CLEANUP;
 }
 dispparamsAuto.cArgs = cArgs;
 dispparamsAuto.cNamedArgs = 0;
 dispparamsAuto.rgvarg = pvAuto;
 dispparamsAuto.rgdispidNamedArgs = NULL;
 if(type & DISPATCH_PROPERTYPUT)
 {
    dispparamsAuto.cNamedArgs = 1;
    dispparamsAuto.rgdispidNamedArgs = &dispidSave;
 }
 hr = pDisp->Invoke(dispidAuto, IID_NULL, LOCALE_SYSTEM_DEFAULT, type, &dispparamsAuto, pvResult, &excepinfoAuto, &uArgErrAuto);
 va_end(marker);

 CLEANUP:
 if(pvAuto)
    free(pvAuto);
 if(pDisp)
    pDisp->Release();

 return hr;
}


HRESULT AutowrapNamedInvoke(VARIANT* pvResult, IDispatch* pDispIn, OLECHAR* szName, int cArgs, ...)
{
 int          i;
 HRESULT      hr;
 DISPPARAMS   dispparamsAuto = {0};
 EXCEPINFO    excepinfoAuto;
 UINT         uArgErrAuto;
 VARIANT*     pvAuto = NULL;
 DISPID*      pdispidAuto;
 LPOLESTR*    szNames;
 IDispatch*   pDisp;
 va_list      marker;

 if(!pDispIn)
    return E_INVALIDARG;
 hr = pDispIn->QueryInterface(IID_IDispatch, (void**)&pDisp);
 if(!SUCCEEDED(hr))
    goto CLEANUP;
 va_start(marker, cArgs);
 pdispidAuto = (DISPID *) malloc(sizeof(DISPID) * (1 + cArgs));
 szNames = (LPOLESTR *) malloc(sizeof(LPOLESTR) * (1 + cArgs));
 if(cArgs > 0)
    pvAuto = (VARIANT *) malloc(sizeof(VARIANT) * cArgs);
 if(!pdispidAuto || !szNames || (!pvAuto && cArgs > 0))
 {
    hr = E_OUTOFMEMORY;
    goto CLEANUP;
 }
 szNames[0] = szName;
 for(i = 0; i < cArgs; ++i)
 {
     VARIANT* pvar;
     szNames[i + 1] = va_arg(marker, OLECHAR*);
     pvar = va_arg(marker, VARIANT*);
     memcpy((pvAuto + i), pvar, sizeof(VARIANT));
 }
 hr = pDisp->GetIDsOfNames(IID_NULL, szNames, cArgs + 1, LOCALE_SYSTEM_DEFAULT, pdispidAuto);
 if(FAILED(hr))
 {
    pvResult = NULL;
    goto CLEANUP;
 }
 dispparamsAuto.cArgs = cArgs;
 dispparamsAuto.cNamedArgs = cArgs;
 if(cArgs > 0)
 {
    dispparamsAuto.rgvarg = pvAuto;
    dispparamsAuto.rgdispidNamedArgs = &pdispidAuto[1];
 }
 hr = pDisp->Invoke(pdispidAuto[0], IID_NULL, LOCALE_SYSTEM_DEFAULT, DISPATCH_METHOD, &dispparamsAuto, pvResult, &excepinfoAuto, &uArgErrAuto);
 va_end(marker);

 CLEANUP:
 if(pdispidAuto)
    free(pdispidAuto);
 if(szNames)
    free(szNames);
 if(pvAuto)
    free(pvAuto);
 if(pDisp)
    pDisp->Release();

 return hr;
}


HRESULT GetDispProperty(IDispatch* pdisp, OLECHAR* szName, VARIANT* pvResult)
{
 return AutowrapInvoke(DISPATCH_PROPERTYGET, pvResult, pdisp, szName, 0);
}


HRESULT PutDispProperty(IDispatch* pdisp, OLECHAR * szName, VARIANT * pvValue, VARIANT * pvResult)
{
 return AutowrapInvoke(DISPATCH_PROPERTYPUT,pvResult, pdisp, szName, 1, pvValue);
}


int main()
{
 HRESULT  hr;
 CLSID  clsid;
 VARIANT  varResult;

 hr = OleInitialize(NULL);
 hr = CLSIDFromProgID(L"Excel.Application", &clsid);
 IDispatch * pApp;
 hr = CoCreateInstance(clsid, NULL, CLSCTX_LOCAL_SERVER, IID_IDispatch, (void**)&pApp);

 VARIANT varVisible; varVisible.vt = VT_BOOL; varVisible.boolVal = VARIANT_TRUE;
 hr = PutDispProperty(pApp, L"Visible", &varVisible, &varResult);

 IDispatch * pWorkbooks;
 hr = GetDispProperty(pApp, L"Workbooks", &varResult);
 MOVE_DISP_RESULT(&varResult, pWorkbooks);

 IDispatch * pWorkbook;
 VARIANT varFilename;
 varFilename.vt = VT_BSTR;
 varFilename.bstrVal =
 SysAllocString
 (
   L"C:\\Code\\CodeBlks\\XL_Dispatch\\Book1.xls" // <<< need to specify path to file!!!
 );
 hr = AutowrapNamedInvoke(&varResult, pWorkbooks, L"Open", 1, L"FileName", &varFilename);
 MOVE_DISP_RESULT(&varResult, pWorkbook);

 IDispatch * pWorksheets;
 hr = GetDispProperty(pWorkbook, L"Worksheets", &varResult);
 MOVE_DISP_RESULT(&varResult, pWorksheets);

 // New Added From CBurn
 IDispatch * pWorksheet;
 VARIANT varSheetname;
 varSheetname.vt = VT_BSTR;
 varSheetname.bstrVal = SysAllocString(L"Sheet2");
 hr=AutowrapInvoke(DISPATCH_PROPERTYGET, &varResult, pWorksheets, L"Item", 1, &varSheetname);
 MOVE_DISP_RESULT(&varResult, pWorksheet);
 // End New Added From CBurn

 VARIANT varReplace; varReplace.vt = VT_BOOL; varReplace.boolVal = VARIANT_TRUE;
 hr = AutowrapNamedInvoke(&varResult, pWorksheet, L"Select", 1, L"Replace", &varReplace);
 getchar();

 pWorksheet->Release();
 pWorksheets->Release();
 pWorkbook->Release();
 pWorkbooks->Release();
 hr = AutowrapNamedInvoke(&varResult, pApp, L"Quit", 0);
 pApp->Release();

 return 0;
}
Pages: 1234