by ゲスト » 2006年2月26日(日) 13:03
オートメーションは私も興味があってやっていることですから,大して苦ではありません。
http://www.discoversoft.net/forum/viewtopic.php?t=775
順調だと思っていたらこのざまです。Runメソッドが山ほど引数を取っていてどうしようもないです。
次善策はここだけ「Visual C++」でDLLを作ることです。
Visual C++はコンパイラのCOMサポートが充実しているので,肝心の部分は意外と簡潔に書けます。
#ABもいつかVC++並にCOMやオートメーションが簡単になってほしいものです。
たとえば元のVBSと同じことを実行するVC++でのコードは次のようになります。
[ここをクリックすると内容が表示されます] [ここをクリックすると非表示にします]これをeem.cppとしてコマンドラインからcl eem.cpp /GX /O2 /MT /FeEEM.DLL /link /DLLでコンパイルしました。
コード: 全て選択
#include <windows.h>
#include <assert.h>
#import "C:\Program Files\Common Files\Microsoft Shared\Office10\MSO.DLL" \
rename("RGB", "RBGXL") \
rename("DocumentProperties", "DocumentPropertiesMSO") \
rename("SearchPath", "SearchPathMSO")
#import "C:\Program Files\Common Files\Microsoft Shared\VBA\VBA6\VBE6EXT.OLB"
#import "C:\Program Files\Microsoft\Office\EXCEL9.OLB" \
rename("RGB", "RBGXL") \
rename("ExitWindows", "WordExitWindows") \
rename("DialogBox", "DialogBoxXL") \
rename("DocumentProperties", "DocumentPropertiesXL") \
rename("ReplaceText", "ReplaceTextXL") \
rename("CopyFile", "CopyFileXL") \
int WINAPI WinMain(HINSTANCE, HINSTANCE, char*, int)
{
if (FAILED(CoInitialize(0)))
return 2;
int ret = 1;
try
{
Excel::_ApplicationPtr xlObj(L"Excel.Application");
xlObj->Workbooks->Open(L"h:\\test.xls");
xlObj->Run(L"test.xls!Auto_open");
xlObj->Quit();
ret = 0;
}
catch (const _com_error& e)
{
MessageBoxW(0, e.Description(), 0, MB_ICONERROR);
}
CoUninitialize();
return ret;
}
tryの中がキモで,あとは準備や後片付けです。
#importはVB/VBAの参照設定に当たるものだと思えば構いません。
また,Excelのインスタンスが残るのが嫌なので,Quitの呼び出しを追加しています。
というわけでDLL化します。
[ここをクリックすると内容が表示されます] [ここをクリックすると非表示にします]このコードをeem.cppとして,コマンドラインからcl eem.cpp /GX /O2 /MT /FeEEM.DLL /link /DLLでコンパイルしました。
コード: 全て選択
#include <string>
#include <cwchar>
#include <windows.h>
#import "C:\Program Files\Common Files\Microsoft Shared\Office10\MSO.DLL" \
rename("RGB", "RBGXL") \
rename("DocumentProperties", "DocumentPropertiesMSO") \
rename("SearchPath", "SearchPathMSO")
#import "C:\Program Files\Common Files\Microsoft Shared\VBA\VBA6\VBE6EXT.OLB"
#import "C:\Program Files\Microsoft\Office\EXCEL9.OLB" \
rename("RGB", "RBGXL") \
rename("ExitWindows", "WordExitWindows") \
rename("DialogBox", "DialogBoxXL") \
rename("DocumentProperties", "DocumentPropertiesXL") \
rename("ReplaceText", "ReplaceTextXL") \
rename("CopyFile", "CopyFileXL") \
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, PVOID)
{
switch (fdwReason)
{
case DLL_PROCESS_ATTACH:
DisableThreadLibraryCalls(hinstDLL);
}
return TRUE;
}
namespace
{
_bstr_t LastErrorMessage;
}
extern "C"
{
__declspec(dllexport)
HRESULT WINAPI ExecExcelMacrosW(PCWSTR FilePath, PCWSTR MacroName)
{
//ファイル名部分だけを取り出す。
PCWSTR pFileName = std::wcsrchr(FilePath, L'\\');
std::wstring RunArg = pFileName ? pFileName + 1 : FilePath;
RunArg += '!';
RunArg += MacroName;
try
{
Excel::_ApplicationPtr xlObj(L"Excel.Application");
xlObj->Workbooks->Open(FilePath);
xlObj->Run(RunArg.c_str());
xlObj->Quit();
}
catch (const _com_error& e)
{
LastErrorMessage = e.Description();
return e.Error();
}
return S_OK;
}
__declspec(dllexport)
HRESULT WINAPI ExecExcelMacrosA(PCSTR FileName, PCSTR MacroName)
{
//_bstr_tのマルチバイト→ワイド文字変換を利用(悪用?)
_bstr_t bsFileName(FileName);
_bstr_t bsMacroName(MacroName);
return ExecExcelMacrosW(bsFileName, bsMacroName);
}
__declspec(dllexport)
PCWSTR WINAPI eemGetLastErrorMessageW()
{
return LastErrorMessage;
}
__declspec(dllexport)
PCSTR WINAPI eemGetLastErrorMessageA()
{
return LastErrorMessage;
}
}
これのABでの宣言はこうなります。
コード: 全て選択
Declare Function ExecExcelMacros Lib "EEM.DLL" Alias "_ExecExcelMacrosA@8" (FilePath As BytePtr, MacroName As BytePtr) As HRESULT
Declare Function ExecExcelMacrosW Lib "EEM.DLL" Alias "_ExecExcelMacrosW@8" (FilePath As *WCHAR, MacroName As BytePtr) As HRESULT
Declare Function ExecExcelMacrosA Lib "EEM.DLL" Alias "_ExecExcelMacrosA@8" (FilePath As BytePtr, MacroName As BytePtr) As HRESULT
Declare Function eemGetLastErrorMessageW Lib "EEM.DLL" Alias "eemGetLastErrorMessageW" () As *WCHAR
Declare Function eemGetLastErrorMessageA Lib "EEM.DLL" Alias "eemGetLastErrorMessageA" () As BytePtr
Declare Function eemGetLastErrorMessage Lib "EEM.DLL" Alias "eemGetLastErrorMessageA" () As BytePtr
たとえばこう使います。
eemGetLastErrorMessageは最後のエラー発生時のエラーメッセージを返す作りになっています。
コード: 全て選択
#strict
Dim hr As HRESULT
hr = CoInitialize(0)
If hr <> S_OK Then
Dim ErrorMsg As String
ErrorMsg = GetErrorStr(hr)
MessageBox(0, StrPtr(ErrorMsg), 0, MB_ICONERROR)
End
End If
If ExecExcelMacros("H:\Test.xls", "Auto_open") <> S_OK Then
MessageBox(0, eemGetLastErrorMessage(), 0, MB_ICONERROR)
End If
CoUninitialize()
End
Const LANG_USER_DEFAULT = MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT)
Function GetErrorStr(dwMessageId As DWord) As String
Dim pszMsg As BytePtr
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER Or FORMAT_MESSAGE_FROM_SYSTEM Or FORMAT_MESSAGE_IGNORE_INSERTS, _
NULL, _
dwMessageId, _
LANG_USER_DEFAULT, _
VarPtr(pszMsg), _
0, _
NULL)
If Not pszMsg = NULL Then
GetErrorStr = pszMsg
LocalFree(pszMsg)
End If
End Function
ところで,NoWestさんのコードでもPrint "Excel起動中"の行さえ削除すれば#console抜きでコンパイルできるはずです。
オートメーションは私も興味があってやっていることですから,大して苦ではありません。
[url]http://www.discoversoft.net/forum/viewtopic.php?t=775[/url]
順調だと思っていたらこのざまです。Runメソッドが山ほど引数を取っていてどうしようもないです。
次善策はここだけ「Visual C++」でDLLを作ることです。
Visual C++はコンパイラのCOMサポートが充実しているので,肝心の部分は意外と簡潔に書けます。
#ABもいつかVC++並にCOMやオートメーションが簡単になってほしいものです。
たとえば元のVBSと同じことを実行するVC++でのコードは次のようになります。
[hide]これをeem.cppとしてコマンドラインからcl eem.cpp /GX /O2 /MT /FeEEM.DLL /link /DLLでコンパイルしました。
[code]#include <windows.h>
#include <assert.h>
#import "C:\Program Files\Common Files\Microsoft Shared\Office10\MSO.DLL" \
rename("RGB", "RBGXL") \
rename("DocumentProperties", "DocumentPropertiesMSO") \
rename("SearchPath", "SearchPathMSO")
#import "C:\Program Files\Common Files\Microsoft Shared\VBA\VBA6\VBE6EXT.OLB"
#import "C:\Program Files\Microsoft\Office\EXCEL9.OLB" \
rename("RGB", "RBGXL") \
rename("ExitWindows", "WordExitWindows") \
rename("DialogBox", "DialogBoxXL") \
rename("DocumentProperties", "DocumentPropertiesXL") \
rename("ReplaceText", "ReplaceTextXL") \
rename("CopyFile", "CopyFileXL") \
int WINAPI WinMain(HINSTANCE, HINSTANCE, char*, int)
{
if (FAILED(CoInitialize(0)))
return 2;
int ret = 1;
try
{
Excel::_ApplicationPtr xlObj(L"Excel.Application");
xlObj->Workbooks->Open(L"h:\\test.xls");
xlObj->Run(L"test.xls!Auto_open");
xlObj->Quit();
ret = 0;
}
catch (const _com_error& e)
{
MessageBoxW(0, e.Description(), 0, MB_ICONERROR);
}
CoUninitialize();
return ret;
}[/code][/hide]
tryの中がキモで,あとは準備や後片付けです。
#importはVB/VBAの参照設定に当たるものだと思えば構いません。
また,Excelのインスタンスが残るのが嫌なので,Quitの呼び出しを追加しています。
というわけでDLL化します。
[hide]このコードをeem.cppとして,コマンドラインからcl eem.cpp /GX /O2 /MT /FeEEM.DLL /link /DLLでコンパイルしました。
[code]
#include <string>
#include <cwchar>
#include <windows.h>
#import "C:\Program Files\Common Files\Microsoft Shared\Office10\MSO.DLL" \
rename("RGB", "RBGXL") \
rename("DocumentProperties", "DocumentPropertiesMSO") \
rename("SearchPath", "SearchPathMSO")
#import "C:\Program Files\Common Files\Microsoft Shared\VBA\VBA6\VBE6EXT.OLB"
#import "C:\Program Files\Microsoft\Office\EXCEL9.OLB" \
rename("RGB", "RBGXL") \
rename("ExitWindows", "WordExitWindows") \
rename("DialogBox", "DialogBoxXL") \
rename("DocumentProperties", "DocumentPropertiesXL") \
rename("ReplaceText", "ReplaceTextXL") \
rename("CopyFile", "CopyFileXL") \
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, PVOID)
{
switch (fdwReason)
{
case DLL_PROCESS_ATTACH:
DisableThreadLibraryCalls(hinstDLL);
}
return TRUE;
}
namespace
{
_bstr_t LastErrorMessage;
}
extern "C"
{
__declspec(dllexport)
HRESULT WINAPI ExecExcelMacrosW(PCWSTR FilePath, PCWSTR MacroName)
{
//ファイル名部分だけを取り出す。
PCWSTR pFileName = std::wcsrchr(FilePath, L'\\');
std::wstring RunArg = pFileName ? pFileName + 1 : FilePath;
RunArg += '!';
RunArg += MacroName;
try
{
Excel::_ApplicationPtr xlObj(L"Excel.Application");
xlObj->Workbooks->Open(FilePath);
xlObj->Run(RunArg.c_str());
xlObj->Quit();
}
catch (const _com_error& e)
{
LastErrorMessage = e.Description();
return e.Error();
}
return S_OK;
}
__declspec(dllexport)
HRESULT WINAPI ExecExcelMacrosA(PCSTR FileName, PCSTR MacroName)
{
//_bstr_tのマルチバイト→ワイド文字変換を利用(悪用?)
_bstr_t bsFileName(FileName);
_bstr_t bsMacroName(MacroName);
return ExecExcelMacrosW(bsFileName, bsMacroName);
}
__declspec(dllexport)
PCWSTR WINAPI eemGetLastErrorMessageW()
{
return LastErrorMessage;
}
__declspec(dllexport)
PCSTR WINAPI eemGetLastErrorMessageA()
{
return LastErrorMessage;
}
}[/code][/hide]
これのABでの宣言はこうなります。
[code]Declare Function ExecExcelMacros Lib "EEM.DLL" Alias "_ExecExcelMacrosA@8" (FilePath As BytePtr, MacroName As BytePtr) As HRESULT
Declare Function ExecExcelMacrosW Lib "EEM.DLL" Alias "_ExecExcelMacrosW@8" (FilePath As *WCHAR, MacroName As BytePtr) As HRESULT
Declare Function ExecExcelMacrosA Lib "EEM.DLL" Alias "_ExecExcelMacrosA@8" (FilePath As BytePtr, MacroName As BytePtr) As HRESULT
Declare Function eemGetLastErrorMessageW Lib "EEM.DLL" Alias "eemGetLastErrorMessageW" () As *WCHAR
Declare Function eemGetLastErrorMessageA Lib "EEM.DLL" Alias "eemGetLastErrorMessageA" () As BytePtr
Declare Function eemGetLastErrorMessage Lib "EEM.DLL" Alias "eemGetLastErrorMessageA" () As BytePtr[/code]
たとえばこう使います。
eemGetLastErrorMessageは最後のエラー発生時のエラーメッセージを返す作りになっています。
[code]#strict
Dim hr As HRESULT
hr = CoInitialize(0)
If hr <> S_OK Then
Dim ErrorMsg As String
ErrorMsg = GetErrorStr(hr)
MessageBox(0, StrPtr(ErrorMsg), 0, MB_ICONERROR)
End
End If
If ExecExcelMacros("H:\Test.xls", "Auto_open") <> S_OK Then
MessageBox(0, eemGetLastErrorMessage(), 0, MB_ICONERROR)
End If
CoUninitialize()
End
Const LANG_USER_DEFAULT = MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT)
Function GetErrorStr(dwMessageId As DWord) As String
Dim pszMsg As BytePtr
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER Or FORMAT_MESSAGE_FROM_SYSTEM Or FORMAT_MESSAGE_IGNORE_INSERTS, _
NULL, _
dwMessageId, _
LANG_USER_DEFAULT, _
VarPtr(pszMsg), _
0, _
NULL)
If Not pszMsg = NULL Then
GetErrorStr = pszMsg
LocalFree(pszMsg)
End If
End Function[/code]
ところで,NoWestさんのコードでもPrint "Excel起動中"の行さえ削除すれば#console抜きでコンパイルできるはずです。