エクセルを取得する。アプリケーションIDispatch*Excelにロードされているdll内
質問
誰もがのホールドを取得する方法を知っていますか Excel.Application
IDispatch*
excelプロセスに関連付けられたポインタ dll
ロードされましたか?
ここで重要なことは、プロセスが次のとおりであるということです excel.exe
, 、そして私が必要とするポインタ 必要があります そのプロセスに属します。実行中のオブジェクトテーブルを使用しても、Excelは最初のインスタンスのみを登録するため、飛行しません。
私はいくつかの低レベルのCOMの策略があることを望んでいますが、私はその分野の専門家ではありません。
解決
編集II コードは、 wtfpl ライセンス版の下にあります。 2.
編集:@ericbrownからのコメントの提案に従って、いくつかのExcelプロセスが現在実行されているときにフィルタリングを許可するためのPIDパラメータを追加します。
rotを使用せずにExcelの "Application"オブジェクトへの作業IDispatch*
を取得することができました。トリックはMSAAを使うことです。私のコードはスタンドアロンコンソールアプリケーションとして機能しますが、DLLインジェクションを介してExcelプロセスでコードが実行された場合、それはうまく機能する可能性があります。あなたは専用のスレッドにいる必要があるかもしれません。あなたが私に教育をDLL注入レベルにプッシュさせたいのかを私に知らせてください。
ウィンドウ7 64bでOKをテストし、Unicodeビルド(32ビットと64ビット)を備えています。 Excelバージョン2010 64ビット(バージョン "14")
「ワークシート」オブジェクトから「アプリケーション」プロパティを介してIDispatchを取得します。結果:開いたワークシートが必要です。良いMSSAウィンドウを見つけるには、最上位Excelフレームウィンドウのクラス名が必要です。 Excel 2010では、「XLMain」です。ワークシートのクラス名は "Excel7"で、それは "標準"のようです。
私はメインのExcelウィンドウから直接作業IDispatch*
を取得できませんでしたが、非常に難しいことはありません。それは、MSAAがメインウィンドウに与えるIDispatch(IDispatchがアプリケーションオブジェクトではない)を照会するために、Excelからの自動化DLLを含む#IMPORTを含めることができます(IDispatchは)
#include <atlbase.h>
#pragma comment( lib, "Oleacc.lib" )
HRESULT GetExcelAppDispatch( CComPtr<IDispatch> & spIDispatchExcelApp, DWORD dwExcelPID ) {
struct ew {
struct ep {
_TCHAR* pszClassName;
DWORD dwPID;
HWND hWnd;
};
static BOOL CALLBACK ewp( HWND hWnd, LPARAM lParam ) {
TCHAR szClassName[ 64 ];
if ( GetClassName( hWnd, szClassName, 64 ) ) {
ep* pep = reinterpret_cast<ep*>( lParam );
if ( _tcscmp( szClassName, pep->pszClassName ) == 0 ) {
if ( pep->dwPID == 0 ) {
pep->hWnd = hWnd;
return FALSE;
} else {
DWORD dwPID;
if ( GetWindowThreadProcessId( hWnd, &dwPID ) ) {
if ( dwPID == pep->dwPID ) {
pep->hWnd = hWnd;
return FALSE;
}
}
}
}
}
return TRUE;
}
};
ew::ep ep;
ep.pszClassName = _TEXT( "XLMAIN" );
ep.dwPID = dwExcelPID;
ep.hWnd = NULL;
EnumWindows( ew::ewp, reinterpret_cast<LPARAM>( &ep ) );
HWND hWndExcel = ep.hWnd;
if ( ep.hWnd == NULL ) {
printf( "Can't Find Main Excel Window with EnumWindows\n" );
return -1;
}
ep.pszClassName = _TEXT( "EXCEL7" );
ep.dwPID = 0;
ep.hWnd = NULL;
EnumChildWindows( hWndExcel, ew::ewp, reinterpret_cast<LPARAM>( &ep ) );
HWND hWndWorkSheet = ep.hWnd;
if ( hWndWorkSheet == NULL ) {
printf( "Can't Find a WorkSheet with EnumChildWindows\n" );
return -1;
}
CComPtr<IDispatch> spIDispatchWorkSheet;
HRESULT hr = AccessibleObjectFromWindow( hWndWorkSheet, OBJID_NATIVEOM, IID_IDispatch,
reinterpret_cast<void**>( &spIDispatchWorkSheet ) );
if ( FAILED( hr ) || ( spIDispatchWorkSheet == 0 ) ) {
printf( "AccessibleObjectFromWindow Failed\n" );
return hr;
}
CComVariant vExcelApp;
hr = spIDispatchWorkSheet.GetPropertyByName( CComBSTR( "Application" ), &vExcelApp );
if ( SUCCEEDED( hr ) && ( vExcelApp.vt == VT_DISPATCH ) ) {
spIDispatchExcelApp = vExcelApp.pdispVal;
return S_OK;
}
return hr;
}
int _tmain(int argc, _TCHAR* argv[])
{
DWORD dwExcelPID = 0;
if ( argc > 1 ) dwExcelPID = _ttol( argv[ 1 ] );
HRESULT hr = CoInitialize( NULL );
bool bCoUnInitializeTodo = false;
if ( SUCCEEDED( hr ) ) {
bCoUnInitializeTodo = true;
CComPtr<IDispatch> spDispatchExcelApp;
hr = GetExcelAppDispatch( spDispatchExcelApp, dwExcelPID );
if ( SUCCEEDED( hr ) && spDispatchExcelApp ) {
CComVariant vExcelVer;
hr = spDispatchExcelApp.GetPropertyByName( CComBSTR( "Version" ), &vExcelVer );
if ( SUCCEEDED( hr ) && ( vExcelVer.vt == VT_BSTR ) ) {
wprintf( L"Excel Version is %s\n", vExcelVer.bstrVal );
}
}
}
if ( bCoUnInitializeTodo ) CoUninitialize();
return 0;
}
. 他のヒント
Exceldna でコードを確認することで、これを行う方法を見つけることができるはずです。このプロジェクトには、拡張子ライブラリからExcelに接続するコードが含まれています。コードはあなたが必要とするより複雑なものになる可能性が高いですが、必要な参照を実装します。
これは私がそれを行う方法です:(@manuellを認めます)。 dispatch_wrapper
クラスです、ここに設定するコンストラクタがあります m_disp_application
:
dispatch_wrapper(void)
{
DWORD target_process_id = ::GetProcessId(::GetCurrentProcess());
if (getProcessName() == "excel.exe"){
HWND hwnd = ::FindWindowEx(0, 0, "XLMAIN", NULL);
while (hwnd){
DWORD process_id;
::GetWindowThreadProcessId(hwnd, &process_id);
if (process_id == target_process_id){
HWND hwnd_desk = ::FindWindowEx(hwnd, 0, "XLDESK", NULL);
HWND hwnd_7 = ::FindWindowEx(hwnd_desk, 0, "EXCEL7", NULL);
IDispatch* p = nullptr;
if (SUCCEEDED(::AccessibleObjectFromWindow(hwnd_7, OBJID_NATIVEOM, IID_IDispatch, (void**)&p))){
LPOLESTR name[1] = {L"Application"};
DISPID dispid;
if (SUCCEEDED(p->GetIDsOfNames(IID_NULL, name, 1U, LOCALE_SYSTEM_DEFAULT, &dispid))){
CComVariant v;
DISPPARAMS dp;
::memset(&dp, NULL, sizeof(DISPPARAMS));
EXCEPINFO ei;
::memset(&ei, NULL, sizeof(EXCEPINFO));
if (SUCCEEDED(p->Invoke(dispid, IID_NULL, LOCALE_SYSTEM_DEFAULT, DISPATCH_PROPERTYGET, &dp, &v, &ei, NULL))){
if (v.vt == VT_DISPATCH){
m_disp_application = v.pdispVal;
m_disp_application->AddRef();
return;
}
}
}
}
}
hwnd = ::FindWindowEx(0, hwnd, "XLMAIN", NULL);
}
}
m_disp_application = nullptr;
}
getProcessName()
小文字を返します。
オフィスアプリケーションは自分の文書をROTに登録するため、最初のもの(すでにrotに登録されている)に添付することができます ROT のidispatchの取得は、document.application.hwnd(これはVBA、 idispatch :: getIdSofNamesとidispatch :: Invoke" with dispatch_propertyget )すべてのExcelのウィンドウハンドルを取得するインスタンス。
すべてのExcelインスタンスのIDispatchとWindowsハンドルの間のマッピングがあります。それはあなた自身のExcelインスタンスを見つける時です。ウィンドウハンドルにgetWindowThReadProcessIdを呼び出してプロセスIDを取得し、次にgetCurrentProcessIdによって返された独自のプロセスIDと比較して、現在のプロセスに属するExcelウィンドウを確認し、現在のExcelアプリケーションを見つけるためにHWNDを調べてください。 idispatchインターフェース