Доступ к моей библиотеке WPF COM из Python
Я хотел бы запустить свое приложение WPF из скрипта Python, однако у меня возникли трудности. Для этого я преобразовал приложение WPF в библиотеку COM следующим образом:
namespace MyWpfApp
{
[Guid("F75D3377-D677-41BF-B3D5-C677C442228F")]
public interface IMyWpfAppInterface
{
void ShowCOMDialog();
void ClickButton1();
void ClickButton2();
}
[ClassInterface(ClassInterfaceType.None)]
[Guid("D936A84B-8B1C-4D62-B090-C06E3EB5EEE9")]
public class MyWpfClass : IMyWpfAppInterface
{
private static Thread m_runDlgThread;
private static MainWindow m_mainWindow = null;
private static Application m_app = null;
public MyWpfClass() { }
public void ShowCOMDialog()
{
m_msgHelper = new MessageHelper();
m_runDlgThread = new Thread(runDlg);
m_runDlgThread.SetApartmentState(ApartmentState.STA);
m_runDlgThread.Start();
}
public void ClickButton1(){// to do}
public void ClickButton2(){// to do}
private void runDlg()
{
Application m_app = new Application();
m_mainWindow = new MainWindow();
m_app.Run(m_mainWindow);
}
}
}
Я установил свою сборку в глобальный кеш сборок и зарегистрировал dll следующим образом
gacutil.exe" /i MyWpfApp.dll
REGASM MyWpfApp.dll /tlb:com.MyWpfApp.tlb
Я проверил, что могу успешно импортировать Typelib и запустить мое приложение WPF из консольного приложения win32.
#include "stdafx.h"
#import "..\MyWpfApp\bin\Debug\com.MyWpfApp.tlb" named_guids raw_interfaces_only
int _tmain(int argc, _TCHAR* argv[])
{
CoInitialize(NULL); //Initialize all COM Components
// <namespace>::<InterfaceName>
MyWpfApp::IMyWpfAppInterfacePtr pDotNetCOMPtr;
// CreateInstance parameters
// e.g. CreateInstance (<namespace::CLSID_<ClassName>)
HRESULT hRes =
pDotNetCOMPtr.CreateInstance(MyWpfApp::CLSID_MyWpfClass);
if (hRes == S_OK)
{
const DWORD cTimeout_ms = 500;
HANDLE hEvent = CreateEvent(0, TRUE, FALSE, 0);
BSTR str;
pDotNetCOMPtr->ShowCOMDialog ();
bool toggle = true;
while(true)
{
DWORD dwWait = WaitForSingleObject(hEvent,cTimeout_ms);
if(toggle)
{
pDotNetCOMPtr->ClickButton1();
toggle = false;
}
else
{
pDotNetCOMPtr->ClickButton2();
toggle = true;
}
}
//call .NET COM exported function ShowDialog ()
}
CoUninitialize (); //DeInitialize all COM Components
return 0;
}
Когда я пытаюсь получить доступ к компоненту COM из Python
import win32com.client
from win32com.client import constants as c
myWpf = win32com.client.Dispatch("MyWpfApp.MyWpfClass")
Это не удается, сообщая
File "C:\Python27\lib\site-packages\win32com\client\dynamic.py", line 114, in _GetGoodDispatchAndUserName
return (_GetGoodDispatch(IDispatch, clsctx), userName)
File "C:\Python27\lib\site-packages\win32com\client\dynamic.py", line 91, in _GetGoodDispatch
IDispatch = pythoncom.CoCreateInstance(IDispatch, None, clsctx, pythoncom.IID_IDispatch)
com_error: (-2147221164, 'Class not registered', None, None)
Я вижу класс "MyWpfApp.MyWpfClass" в реестре, а также из OLE Viewer. Я много раз делал подобные вещи для приложений на C++ без каких-либо проблем. Однако это было либо с проектом ATL, либо с приложением MFC с включенной автоматизацией. В этом случае я преобразовал приложение WPF в dll и вручную преобразовал в COM. Может кто-нибудь посоветовать, что еще мне нужно сделать, чтобы запустить приложение из python?
Заранее спасибо.
Дальнейшее редактирование: я могу загрузить typelib в python, но все еще не могу получить доступ к com-классу, который я запускал
import win32com.client
import pythoncom
myApp = pythoncom.LoadTypeLib("D:\\MyWorkSpace\\testProgs\\ATL_COM\\WPF\MyWpfApp\\MyWpfApp\\bin\\Debug\\com.MyWpfApp.tlb")
downloads_stat = None
for index in xrange(0, myApp.GetTypeInfoCount()):
type_name = myApp.GetDocumentation(index)[0]
type_iid = myApp.GetTypeInfo(index).GetTypeAttr().iid
print type_iid
print type_name
if type_name == 'MyWpfClass':
downloads_stat = win32com.client.Dispatch(type_iid)
Это подтверждает, что классы загружены, но я не могу получить к ним доступ, потому что они зарегистрированы как не зарегистрированные.
>>>
{B5E3A6C6-09A0-315C-BF3A-CB943389F610}
MessageHelper
{FBE23BB0-3EDC-3A65-90EB-DF84F7545D70}
COPYDATASTRUCT
{8BEE824F-F708-3052-BC21-A9EC4E1BB002}
MainWindow
{8C0044EF-91A9-3CB3-9945-1ACA076F3D7E}
NextPrimeDelegate
{F75D3377-D677-41BF-B3D5-C677C442228F}
IMyWpfAppInterface
{D936A84B-8B1C-4D62-B090-C06E3EB5EEE9}
MyWpfClass
Traceback (most recent call last):
File "D:\MyWorkSpace\testProgs\ATL_COM\WPF\MyWpfApp\MyWin32App\Python\MyPythonClient.py", line 15, in <module>
downloads_stat = win32com.client.Dispatch(type_iid)
File "C:\Python27\lib\site-packages\win32com\client\__init__.py", line 95, in Dispatch
dispatch, userName = dynamic._GetGoodDispatchAndUserName(dispatch,userName,clsctx)
File "C:\Python27\lib\site-packages\win32com\client\dynamic.py", line 114, in _GetGoodDispatchAndUserName
return (_GetGoodDispatch(IDispatch, clsctx), userName)
File "C:\Python27\lib\site-packages\win32com\client\dynamic.py", line 91, in _GetGoodDispatch
IDispatch = pythoncom.CoCreateInstance(IDispatch, None, clsctx, pythoncom.IID_IDispatch)
com_error: (-2147221164, 'Class not registered', None, None)
>>>
Я также могу подтвердить, что в реестре есть запись "MyWpfApp.MyWpfClass" с идентификатором класса {D936A84B-8B1C-4D62-B090-C06E3EB5EEE9}.
Дальнейшее редактирование:
Я попытался установить Python для.Net и выполнил следующую команду
import clr;
import sys
sys.path.append('D:\\MyWorkSpace\\testProgs\\ATL_COM\\WPF\\MyWpfApp\\MyWpfApp\\bin\\Debug')
clr.AddReference("MyWpfApp.dll")
Однако это привело к следующей ошибке
FileNotFoundException: Unable to find assembly 'MyWpfApp.dll'.
at Python.Runtime.CLRModule.AddReference(String name) in C:\Users\Barton\Documents\Visual Studio 2008\Projects\PySharp\trunk\pythonnet\src\runtime\moduleobject.cs:line 375
Мои знания COM, сборок и библиотек типов весьма ограничены, поэтому я был бы признателен, если бы кто-нибудь помог мне понять, что мне нужно сделать, чтобы получить доступ к DLL из python.
Когда я смотрю на запись в OLE/COM Object viewer, она указывает на файл tlb, а не на dll, т.е.
Win32=D:\MyWorkSpace\testProgs\ATL_COM\WPF\MyWpfApp\MyWpfApp\bin\Debug\com.MyWpfApp.tlb
Таким образом, регистрируется только файл tlb, а не класс. Когда я запускаю класс LoadTypeLib, я загружаю его из пути на моем жестком диске. Мне нужно, чтобы DLL была зарегистрирована, когда я пытаюсь отправить. В противном случае, если бы я мог получить доступ к этому напрямую с фиксированного пути, это также было бы хорошо, но я не знаю, как это сделать.
Еще раз спасибо за вашу помощь.