// Von Exe-Programm Excel starten und neue Seite,
// 225 Zellen mit Stringdaten füttern, Zeit messen.
#include "main.h"
#include <ole2.h>
#include <malloc.h> // _alloca(), erfordert msc.c oder ___chkstk_ms
HINSTANCE ghInstance;
TCHAR MBoxTitle[64];
int vMBox(HWND hParent, UINT uType, const char*msg, va_list va) {
TCHAR s[125],t[125];
if (IS_INTRESOURCE(msg)) LoadString(ghInstance,UINT(size_t(msg)),t,elemof(t));
else
#if UNICODE
MultiByteToWideChar(CP_ACP,0,msg,-1,t,elemof(t));
#else
lstrcpyn(t,msg,elemof(t));
#endif
wvnsprintf(s,elemof(s),t,va);
return MessageBox(hParent,s,MBoxTitle,uType);
}
int _cdecl MBox(HWND hParent, UINT uType, const char*msg, ...) {
va_list va;
va_start(va,msg);
int ret=vMBox(hParent,uType,msg,va);
va_end(va);
return ret;
}
/***************
* Debug-Hilfe *
***************/
static void _cdecl DbgMsg(const char*t,...) {
va_list va;
va_start(va,t);
vMBox(0,MB_OK|MB_ICONEXCLAMATION,t,va);
va_end(va);
}
#ifdef _DEBUG
# define DbgMsg(x) DbgMsg x
#else
# define DbgMsg(x)
#endif
/*************
* OLE-Hilfe *
*************/
HRESULT ghResult;
// VBScript CreateObject() mit Argument in UTF-8
static IDispatch*CreateObject(const char*name) {
if (!name || !*name) return 0;
int l=MultiByteToWideChar(CP_UTF8,0,name,-1,0,0);
WCHAR*pName=(WCHAR*)_alloca(l<<1);
MultiByteToWideChar(CP_UTF8,0,name,-1,pName,l);
CLSID clsid;
if (ghResult=CLSIDFromProgID(pName,&clsid)) {
DbgMsg(("CLSIDFromProgID(\"%s\") failed with error 0x%08lx",name,ghResult));
return 0;
}
IDispatch*pApp;
if (ghResult=CoCreateInstance(clsid,0,CLSCTX_LOCAL_SERVER,IID_IDispatch,(void**)&pApp)) {
DbgMsg(("CoCreateInstance(\"%s\") failed with error 0x%08lx",name,ghResult));
return 0;
}
return pApp;
}
/************************
* Variant raubkopieren *
************************/
static void VariantMove(VARIANT*a,VARIANT*b) {
if (a==b) return; // Nichts tun bei gleichen Zeigern (bei __$ReturnUdt-Rückgabe)
*a=*b; // memcpy()
VariantInit(b); // b.vt=0, sonst nichts
}
static void VariantSwap(VARIANT*a,VARIANT*b) {
if (a==b) return; // Nichts tun bei gleichen Zeigern (sollte nie vorkommen)
VARIANT c=*a;
*a=*b;
*b=c;
}
/********************************
* Automatisch freigebende *
* VARIANT-Struktur *
********************************/
struct AutoVariant:public VARIANT{
AutoVariant() {VariantInit(this);}
AutoVariant(bool v) {vt=VT_BOOL; boolVal=v?-1:0;}
AutoVariant(int v) {vt=VT_I4; lVal=v;} // Zahl-Zuweisung
AutoVariant(const char*s) {Konstrukt(s,-1);} // String-Zuweisung: String endet mit NUL
AutoVariant(const char*s,int l) {Konstrukt(s,l);} // String-Zuweisung mit Länge = kann NUL enthalten
// Verschiebe-Konstruktor und Verschiebe-Zuweisung steht bei MSVC6 nicht zur Verfügung!
AutoVariant(VARIANT&o) {VariantMove(this,&o);}
AutoVariant(AutoVariant&o) {VariantMove(this,&o);}
~AutoVariant() {ghResult=VariantClear(this);}
#if __cplusplus >= 201100
AutoVariant(VARIANT&&o) {VariantMove(this,&o);} // Raubkopier-Konstruktor
AutoVariant(AutoVariant&&o) {VariantMove(this,&o);} // Raubkopier-Konstruktor
void operator=(const VARIANT&o) = delete;
void operator=(const AutoVariant&o) = delete;
#endif
private:
void Konstrukt(const char*s,int l);
};
void AutoVariant::Konstrukt(const char*s,int l) {
int ul=MultiByteToWideChar(CP_UTF8,0,s,l,0,0);
if (l<0) --ul; // ohne Null am Ende
vt = VT_BSTR;
bstrVal = SysAllocStringLen(0,ul);
MultiByteToWideChar(CP_UTF8,0,s,l,bstrVal,ul);
}
AutoVariant vformat(const char*t,va_list va) {
char s[256];
int l=wvnsprintfA(s,sizeof s,t,va);
return AutoVariant(s,l);
}
AutoVariant _cdecl format(const char*t,...) {
va_list va;
va_start(va,t);
return vformat(t,va);
}
/********************************
* Automatisch freigebendes *
* Dispatch-Variant *
********************************/
struct AutoDispatch:public AutoVariant{
// AutoDispatch() {VariantInit(this);}
inline AutoDispatch(VARIANT&o) {if (o.vt==VT_DISPATCH) VariantMove(this,&o); else VariantInit(this);}
#if __cplusplus >= 201100
inline AutoDispatch(VARIANT&&o) {if (o.vt==VT_DISPATCH) VariantMove(this,&o); else VariantInit(this);}
#endif
inline AutoDispatch(const char*name) {vt=(pdispVal=CreateObject(name))?VT_DISPATCH:0;}
// ~AutoDispatch() {VariantClear() ruft pdispVal->Release();}
// Eigenschafts- und Funktionszugriff.
inline AutoVariant Get(const char*name) {
return vWrap(DISPATCH_PROPERTYGET,name,0,0);
}
inline AutoVariant Get(const char*name,AutoVariant arg) {
return vWrap(DISPATCH_PROPERTYGET,name,1,&arg);
}
inline void Put(const char*name,AutoVariant arg) {
vWrap(DISPATCH_PROPERTYPUT,name,1,&arg);
}
// Für Funktionsaufrufe braucht es ggf. mehr als 0 oder 1 Parameter
inline AutoVariant operator() (const char*name) {
return vWrap(DISPATCH_METHOD,name,0,0);
}
inline AutoVariant operator() (const char*name,AutoVariant arg) {
return vWrap(DISPATCH_METHOD,name,1,&arg);
}
inline operator bool() {return vt==VT_DISPATCH;}
private:
// vWrap() - Automation helper function
// Can query or set (named) parameters and call named functions
AutoVariant vWrap(int autoType,const char*name,int argc=0,AutoVariant*argv=0);
};
AutoVariant AutoDispatch::vWrap(int autoType,const char*name,int argc,AutoVariant*argv) {
AutoVariant UDT(ret);
VariantInit(&ret); // Wird vom Aufrufer anscheinend nicht default-konstruiert
DISPID dispID;
int l = MultiByteToWideChar(CP_UTF8,0,name,-1,0,0);
WCHAR*pName = (WCHAR*)_alloca(l<<1);
MultiByteToWideChar(CP_UTF8,0,name,-1,pName,l);
// Get DISPID for name passed...
ghResult = pdispVal->GetIDsOfNames(IID_NULL,&pName,1,LOCALE_USER_DEFAULT,&dispID);
if (FAILED(ghResult)) {
DbgMsg(("IDispatch::GetIDsOfNames(\"%s\") failed with error 0x%08lX",name,ghResult));
}else{
DISPID dispidNamed = DISPID_PROPERTYPUT;
// Variables used...
DISPPARAMS dp = {
argv,
&dispidNamed,
(UINT)argc,
autoType&DISPATCH_PROPERTYPUT ? 1U : 0
};
// Make the call!
ghResult = pdispVal->Invoke(dispID,IID_NULL,LOCALE_SYSTEM_DEFAULT,autoType,&dp,&ret,0,0);
if (FAILED(ghResult)) {
DbgMsg(("IDispatch::Invoke(\"%s\"=%08lx) failed with error 0x%08lx",name,dispID,ghResult));
}
}
return ret;
}
/*****************
* Hauptprogramm *
*****************/
// Es zeigt wie lahm Excel startet
// aber auch wie schnell große Datenmengen
// in Excel-Tabellen eingesetzt werden können!
// Letzteres ist in VBA und VBScript kaum so schnell.
static int mymain() {
if (!CoInitialize(0)) {
DWORD t0=GetTickCount(),t1,t2;
// Keine Zeiger, nur Autopointer!
// Mit den Autopointern sieht dieses Programm schon skript-ähnlich aus.
AutoDispatch XlApp("Excel.Application");
if (XlApp) {
// Der <true>-Parameter ruft den AutoVariant-Konstruktor auf.
XlApp.Put("Visible",true);
// Und das Semikolon ruft den AutoVariant-Destruktor!
// Denn damit endet der Gültigkeitbereich des anonymen AutoVariant.
AutoDispatch(XlApp.Get("ErrorCheckingOptions")).Put("BackgroundChecking",false);
// Get Workbooks collection
// Da AutoDispatch von AutoVariant erbt, braucht es hier keinen Typecast.
// Eine Zuweisung ruft hier den Raubkopier-Konstruktor auf
AutoDispatch XlBooks(XlApp.Get("Workbooks"));
// Call Workbooks.Add() to get a new workbook...
AutoDispatch XlBook(XlBooks.Get("Add"));
// Get ActiveSheet object
{AutoDispatch XlSheet(XlApp.Get("ActiveSheet"));
t1=GetTickCount();
// Create a 15x15 safearray of variants...
{AutoVariant arr;
arr.vt = VT_ARRAY | VT_VARIANT;
SAFEARRAYBOUND sab[2];
sab[0].lLbound = 1; sab[0].cElements = 15;
sab[1].lLbound = 1; sab[1].cElements = 15;
arr.parray = SafeArrayCreate(VT_VARIANT, 2, sab);
// Fill safearray with values...
for(int i=1; i<=15; i++)
for(int j=1; j<=15; j++) {
// Create entry value for (i,j)
// Die printf-ähnliche Funktion generiert sogleich eine BSTR-Variante.
// Add to safearray...
long indices[] = {i,j};
// Anonyme Variante mit Konstruktor in format() ... Zeigerreferenz eines temporären Objekts verboten in C++11
AutoVariant t = format("'%u",i*j);
SafeArrayPutElement(arr.parray,indices,&t);
} // Diese Klammer ruft den Destruktor von t und arr
// Get Range object for the Range A1:O15...
AutoDispatch XlRange(XlSheet.Get("Range","A1:O15"));
// Set range with our safearray...
XlRange.Put("Value",arr);
} // Diese Klammer ruft den Destruktor von <arr> und <XlRange>
t2=GetTickCount();
AutoDispatch(XlSheet.Get("Columns","A:O"))("AutoFit");
}
// Set .Saved property of workbook to TRUE so we aren't prompted
// to save when we tell Excel to quit...
if (false) XlBook.Put("Saved",true);
else XlApp.Put("DisplayAlerts",false);
// Inform and wait for user...
MBox(0,MB_OK|MB_ICONINFORMATION,(const char*)2,DWORD(t1-t0),DWORD(t2-t1)); // "Startup time, fill time"
// Die Startleiste stellt währenddessen ohne Zutun das App-Icon dar
// Tell Excel to quit (i.e. App.Quit)
XlApp("Quit");
}else{
MBox(0,MB_OK|MB_ICONEXCLAMATION,(const char*)3); // "No excel automation server found"
}
// Uninitialize COM for this thread...
CoUninitialize();
}else DbgMsg(("CoInitialize failed with error %d",GetLastError()));
return 0;
}
#ifdef _MSC_VER
void CALLBACK
#else //mingw
extern "C" void ___chkstk_ms() {}
// Genauso wie bei MSVC wird _alloca() mit einer Hilfsfunktion compiliert.
// ___chkstk_ms bekommt in rax die benötigte Stack-Größe
// bereits auf volle 16 Byte aufgerundet.
// Müsste den Stack in 4-KByte-Stückelung touchen.
// Liefert rax unverändert zurück.
// Da die Allokationsgrößen bei _alloca() in diesem Programm mickrig sind,
// tut diese Funktion hier einfach nichts.
extern "C" void
#endif
WinMainCRTStartup() {
ghInstance = GetModuleHandle(0);
LoadString(ghInstance,1,MBoxTitle,elemof(MBoxTitle));
ExitProcess(mymain());
}
Detected encoding: UTF-8 | 0
|