Accessing my WPF COM Library from Python - c#

I'd like to run my WPF application from a python script, however am having difficulty.
To achieve this I have converted the WPF application to a COM library as follows:
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);
}
}
}
I have installed my assembly to the global assembly cache and registered the dll as follows
gacutil.exe" /i MyWpfApp.dll
REGASM MyWpfApp.dll /tlb:com.MyWpfApp.tlb
I have tested that I can successfully import the Typelib and run my WPF application from a win32 console application.
#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;
}
When I attempt to access the COM component from Python
import win32com.client
from win32com.client import constants as c
myWpf = win32com.client.Dispatch("MyWpfApp.MyWpfClass")
This fails, reporting
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)
I can see the class "MyWpfApp.MyWpfClass" int the registry and also from OLE Viewer.
I've done this kind of thing many times for a C++ based application without any problems. However this was with either an ATL project or MFC app with automation switched on. In this case I have converted WPF application to a dll and converted to COM by hand.
Could someone please advise what else I need to do to run the app from python?
Thanks in advance.
Further Edit :
I can load the typelib in python but still can't access the com class
I ran
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)
This confirms that the classes are loaded but I can't access them because they are reported as not registered.
>>>
{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)
>>>
I can also confirm that there is an entry 'MyWpfApp.MyWpfClass' in the registry with the class id {D936A84B-8B1C-4D62-B090-C06E3EB5EEE9}.
Further Edit:
I tried installing Python for .Net and ran the following command
import clr;
import sys
sys.path.append('D:\\MyWorkSpace\\testProgs\\ATL_COM\\WPF\\MyWpfApp\\MyWpfApp\\bin\\Debug')
clr.AddReference("MyWpfApp.dll")
However this produced the following error
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
My knowledge of COM, Assemblies and typelibs are quite limited so I would appreciate if someone could help me understand what I need to do to access the DLL from python.
When I look at the entry in OLE/COM Object viewer, it points to the tlb file, not the dll, i.e.
Win32=D:\MyWorkSpace\testProgs\ATL_COM\WPF\MyWpfApp\MyWpfApp\bin\Debug\com.MyWpfApp.tlb
So, it is only the tlb file that is registered, not the class. When I run the LoadTypeLib class I am loading this from a path on my hard drive. I need the dll to be registered when I attempt to dispatch. Otherwise, if I can access this directly from a fixed path this would also be good but I don't know how to do this.
Again, thanks for your help.

Related

Inno Setup - External .NET DLL with dependencies

I am trying to use a custom DLL in a Inno Setup script during installation. I wrote a very simple function that basically checks a connection string for a MySQL database using MySQL .NET connector (there is no MySQL client on the target server). The code of this exported function is:
public class DbChecker
{
[DllExport("CheckConnexion", CallingConvention.StdCall)]
public static int CheckConnexion([MarshalAs(UnmanagedType.LPStr)] string connexionString)
{
int success;
try
{
MySqlConnection connection = new MySqlConnection(connexionString);
connection.Open();
connection.Close();
success = 0;
}
catch (Exception)
{
success = 1;
}
return success;
}
}
The function is imported this way in Inno Setup :
[Files]
Source: "..\..\MyDll\bin\x86\Release\*"; Flags: dontcopy;
and
[Code]
function CheckConnexion(connexionString: AnsiString): Integer;
external 'CheckConnexion#files:MyDll.dll,MySql.Data.dll stdcall setuponly loadwithalteredsearchpath';`
The problem is that the setup throws an exception at runtime:
Runtime Error (at 53:207):
External exception E0434352.
I think I have to use the files prefix because the function is called in the NextButtonClick event handler, before files are copied to the {app} directory.
Both MyDll.dll and MySql.Data.dll are correctly extracted to the {tmp} directory at runtime.
I tried both with and without the loadwithalteredsearchpath flag with the same result.
What I found is that this error code is a generic .NET runtime error code.
If I remove the part using MySql.Data it works perfectly fine (except that it does nothing...)
As advised on other threads I've been trying to log the error in my .NET code using EventLog and UnhandledException but I have the same exception no matter what (and no log source is created), even without the MySQL part. I checked EventLog permissions on my computer.
It seems that the exception is thrown as soon as I use anything else that "basic" C# code (whenever I try to load another DLL).
There is probably a better way, but this will do.
Implement an initialization function (Init here) that sets up AppDomain.AssemblyResolve handler that looks for an assembly in the path of the main (executing) assembly:
[DllExport("Init", CallingConvention.StdCall)]
public static void Init()
{
AppDomain currentDomain = AppDomain.CurrentDomain;
currentDomain.AssemblyResolve += new ResolveEventHandler(MyResolveEventHandler);
}
private static Assembly MyResolveEventHandler(object sender, ResolveEventArgs args)
{
string location = Assembly.GetExecutingAssembly().Location;
AssemblyName name = new AssemblyName(args.Name);
string path = Path.Combine(Path.GetDirectoryName(location), name.Name + ".dll");
if (File.Exists(path))
{
return Assembly.LoadFrom(path);
}
return null;
}
Import it to the Inno Setup:
procedure Init(); external 'Init#files:MyDll.dll stdcall setuponly';
And call it before calling the function that needs the dependency (CheckConnexion).
Another solution might be this:
Embedding DLLs in a compiled executable
Btw, no need for the loadwithalteredsearchpath flag. It has no effect on .NET assemblies imo. They are needed for native DLL dependencies: Loading DLL with dependencies in Inno Setup fails in uninstaller with "Cannot import DLL", but works in the installer.
I found something else that might be helpful for anyone stumbling upon this page.
In my scenario, I have several C# methods that I call from InnoSetup using DllExport. In one of those methods, I call another of the methods. This caused Inno to throw "External exception E0434352".
If I moved the code to a method not called by InnoSetup, everything worked fine.
So...
[DllExport("Fu", CallingConvention = CallingConvention.StdCall)]
public static int Fu()
{
// Stuff
}
[DllExport("Bar", CallingConvention = CallingConvention.StdCall)]
public static int Bar()
{
Fu();
}
...causes InnoSetup to cry, but:
[DllExport("Fu", CallingConvention = CallingConvention.StdCall)]
public static int Fu()
{
LocalFu();
}
private static int LocalFu()
{
// Stuff
}
[DllExport("Bar", CallingConvention = CallingConvention.StdCall)]
public static int Bar()
{
// Stuff
LocalFu();
// Other stuff
}
...is fine.
I don't know if this is caused by Inno or DllExport, so I'll forgo direct derision and blame society as a whole for my lost morning. (Or myself for being a new to this thing.)
I would like to expand upon Martin's answer. There is a way to resolve the assemblies without having to call an Init method first and that is by including a static constructor in your .NET class:
public class MyClass
{
static MyClass()
{
AppDomain currentDomain = AppDomain.CurrentDomain;
currentDomain.AssemblyResolve += MyResolveEventHandler;
}
private static Assembly MyResolveEventHandler(object sender, ResolveEventArgs args)
{
var location = Assembly.GetExecutingAssembly().Location;
var assemblyName = new AssemblyName(args.Name);
var path = Path.Combine(Path.GetDirectoryName(location), assemblyName.Name + ".dll");
if (File.Exists(path))
{
return Assembly.LoadFrom(path);
}
return null;
}
}

BadImgeFormatException when trying to run application after reinstalling Windows 8.1

I hope this question will not be regarded as a duplicate as I know there are many similar questions on stackoverflow. I should know. I already read most of them. So bear with me...
I am writing a C# application that relies on the OpenCV libraries to function. The OpenCV libraries are written in c++ code and to use them in c#, I wrote 2 libraries: a static library containing the methods I want from OpenCV; a CLR DLL that acts like a bridge between the static lib and the C# project.
What perplexes me is the fact that everything worked fine until I reinstalled Windows 8.1 due to a virus. Before the reinstall, the app compiled and ran just like intended. After the reinstall, the very same project throws a "BadImageFormatException" when trying to debug:
namespace G19_GUI
{
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new GUI.GUI()); //this is the line producing the exception
}
}
}
 
Additional information: Could not load file or assembly 'clrLib, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null' or one of its dependencies. An attempt was made to load a program with an incorrect format.
I know the most common cause for this exception is that the application is trying to load a 32bit library into a 64bit compiled application or vice-versa. As such, I verified that the OpenCV libraries I am trying to use are destined for use in a 32bit application and indeed they are. I tried switching to the 64bit dlls, action that produced 39 errors of type "LINK2028: unresolved token".
Keep in mind that the project itself did not change at all between the 2 installations of Windows. I used the same property sheet and the same OpenCV libraries. Everything was kept on an external drive, which was unplugged at the time I foolishly double clicked the virus executable, and backed up on DropBox, the only thing I had to do after reinstalling Windows was to reset the path and OpenCV environment variables, which I did using a .bat file, the same .bat file used to set those very same variables in the first install of Windows. Therefore, I highly doubt the possibility of the virus messing with my project files. Even so, I double-checked everything and couldn't find anything wrong.
I proceeded to documenting the exception online. As a result, I ended up trying every possible combination of configuration builds and target platforms I dared have the patience to try for all of the 3 projects I had in my solution.
Indeed, trying to build and compile for a 64bit machine removed the exception but another DLL my C# project depended on was destined for 32bit use and when trying to load that DLL, the very same exception popped up (which was expected). Unfortunately, the DLL did not come with a 32bit version or with a source code to try and build my own 64bit version of said DLL.
Thus, I am forced to build my application either for both target platforms (any cpu, mixed platform, whatever) or only for 32bit. Which, of course, produces the exception at start-up.
I know all my DLLs are made for 32bit use. For the life of me, I cannot figure out what the problem is, nor why this problem did not exist before I reinstalled the os.
Below is the code of my clrLib dll:
#include "Stdafx.h"
#include "clrLib.h"
#include <vector>
namespace clrLib
{
public ref class mapper
{
private:
static openCVProc * myProc;
public:
static mapper(void)
{
myProc = new openCVProc();
}
char * mapStringToChar(String ^ path)
{
return (char*)(void*)Marshal::StringToHGlobalAnsi(path);
}
array<int>^ getRGBfromHSV(int h, int s, int v)
{
array<int>^ values = gcnew array<int>(3);
std::vector<int> myVals = myProc->hsv2rgb(h, s, v);
values[0] = myVals[0];
values[1] = myVals[1];
values[2] = myVals[2];
return values;
}
array<int>^ getHSV(String^ src)
{
array<int>^ vals = gcnew array<int>(3);
vals[0] = (myProc->getHSV(mapper::mapStringToChar(src)))[0];
vals[1] = (myProc->getHSV(mapper::mapStringToChar(src)))[1];
vals[2] = (myProc->getHSV(mapper::mapStringToChar(src)))[2];
return vals;
}
void openCam()
{
myProc->startCam();
}
void closeCam()
{
myProc->stopCam();
}
int^ getBrightness()
{
int^ b;
b = myProc->getB();
return b;
}
bool^ camState()
{
return myProc->camState();
}
array<Byte>^ mapper::getFrameData()
{
cv::Mat f = myProc->getFrame();
int width = f.cols;
int height = f.rows;
array<Byte>^ bitmapData = gcnew array<Byte>(width*height * 3);
int c = 0;
for (int i = 0; i < height; i++)
{
for (int j = 0; j < width; j++)
{
bitmapData[c] = f.at<cv::Vec3b>(i, j)[0];
bitmapData[c + 1] = f.at<cv::Vec3b>(i, j)[1];
bitmapData[c + 2] = f.at<cv::Vec3b>(i, j)[2];
c += 3;
}
}
return bitmapData;
}
Size^ mapper::getFrameSize()
{
std::vector<int> size = myProc->getFrameSize();
Size^ frameSize = gcnew Size(size[0], size[1]);
return frameSize;
}
};
}
The other 2 libraries are a bit lengthy, but I will include them if need be.
Thank you for taking the time to read my post.

Mono: Load an assembly from an injected dll on a process

I'm trying to load an assembly from an injected dll ( c++ ) ,
First I hook :
pMono1 = (Mono1Fn)DetourFunction((PBYTE)GetProcAddress(addr,"mono_assembly_load_from_full"), (PBYTE)Mono1);
pMono2 = (Mono2Fn)GetProcAddress(addr, "mono_image_open_full");
mono_jit_exec_ = (mono_jit_exec_t)GetProcAddress(addr,"mono_jit_exec");
pMonoDomainGet = (mono_domain_get_t)GetProcAddress(addr,"mono_domain_get");
(I've checked that addr is correct )
Then what I do is
MonoAssembly* __cdecl Mono1(MonoImage *image, const char*fname, MonoImageOpenStatus *status, gboolean refonly)
{
if(!Loaded)
{
Loaded = true;
OpenAssembly();
}
return pMono1(image,fname,status,refonly);
}
So, what I'm trying to do is:
When the target process opens an assembly, I open mine and the original assembly to load
But the problem is : the opened assembly doesnt run.
Here is the method OpenAssembly():
void OpenAssembly()
{
MonoImageOpenStatus status = MONO_IMAGE_ERROR_ERRNO;
MonoImage *Image = (MonoImage*)pMono2("B:\\AsmTest.dll",&status,FALSE);
MonoAssembly *Assm = (MonoAssembly*)Mono1(Image,"B:\\AsmTest.dll",&status,FALSE);
char* in[] = {"B:\\AsmTest.dll" };
cout << "In[0] : " << in[0] << endl;
mono_jit_exec_((MonoDomain*)pMonoDomainGet(),Assm,0,in);
}
In the embedding mono tutorial they say that mono_jit_exec calls the Main method,
but it doesnt,( which leads me to think that the error is in my assembly ),
Here the code of the simple Assembly :
public class AssemblyTest
{
static void Main()
{
MessageBox.Show("I do WORK :d");
}
}
So like I said before , my problem is that the Main() method never gets called in the process.(Because the MessageBox doesnt show up)
For your information : I've put the assembly in B disk.
I think my problem is so simple, but I cant find why.
Thanks in advance, I hope you understand what I mean :)

Marshalling C++ pointer interface back though C# function call in a non default AppDomain

I have a working CLI interface between C++ and C# code. The code has a C++ abstract interface like:
-------------C++ Interface---------------
namespace cppns
{
class cppInterface
{
public:
virtual bool Start(const char *pcDir) = 0;
};
}
------Implementation of abstract C++ interface in same dll---------
namespace cppns
{
class cppimp : public cppInterface
private:
gcroot<MyInternalCSharpClass^> mInternalClassAccess;
public:
cppimp::cppimp()
{
mInternalClassAccess = gcnew MyInternalCSharpClass();
}
virtual bool cppimp::Start(const char *pcDir)
{
System::AppDomain ^appDom = AppDomain::CurrentDomain::get();
System::String ^strDomainName = appDom->FriendlyName;
mInternalClassAccess->Initalize(pcDir);
}
}
---------Method to create an instance of the class in a factory--------------
cppns::cppInterface *GetImplObject()
{
return new cppns::cppimp();
}
----------Factory class .h to allow C++ to get an instance of the cppimp class------
------The C++ code knows about the abstract interface by including the header file--
------FactoryExport is __declspec(dllexport) when compiled in dll and---------------
----- __declspec(dllimport) when used as a header file in exe that uses header------
class FactoryExport ClassFactory
{
public:
static cppns::cppInterface *CreateImpl();
};
----------Factory class .cpp to allow C++ to get an instance of the cppimp class------
cppns::cppInterface *ClassFactory::CreateImpl()
{
return GetImplObject();
}
This code correctly allows me to call CreateImpl to get an implementation of the interface that contains the Start method. My issue is that I'm trying to force the whole CLR/.NET loading and executing into an AppDomain that is not the default AppDomain. I can create a secondary AppDomain using the following code:
CComPtr<ICorRuntimeHost> pRuntimeHost;
//Retrieve a pointer to the ICorRuntimeHost interface
HRESULT hr = CorBindToRuntimeEx(
L"v2.0.50727", //Retrieve last version before 4.0.
// NULL, //Retrieve latest version by default
L"wks",
STARTUP_LOADER_OPTIMIZATION_SINGLE_DOMAIN | STARTUP_CONCURRENT_GC,
CLSID_CorRuntimeHost,
IID_ICorRuntimeHost,
(void**)&pRuntimeHost.p
);
hr = pRuntimeHost->Start();
DWORD dwAppDomainId = 22;
WCHAR domainName[80 + 1];
swprintf(domainName, 80, L"%s-%ld",L"NoDefaultDomain", dwAppDomainId);
CComPtr<IUnknown> pUnknownAppDomain;
hr = pRuntimeHost->CreateDomainEx(domainName, NULL, NULL, &pUnknownAppDomain);
CComPtr<_AppDomain> pAppDomain;
hr = pUnknownAppDomain->QueryInterface(__uuidof(_AppDomain), (VOID**)&pAppDomain.p);
BSTR bstrFriendlyName;
hr = pAppDomain->get_FriendlyName(&bstrFriendlyName);
if (SUCCEEDED(hr))
{
_bstr_t bstrFriendlyNameWrap(bstrFriendlyName, false);
}
_bstr_t bstrAssemblyName("InteropCode");
CComPtr<_Assembly> pAssembly;
hr = pAppDomain->Load_2(bstrAssemblyName, &pAssembly);
BSTR bstrFullName;
hr = pAssembly->get_FullName(&bstrFullName);
if (SUCCEEDED(hr))
{
_bstr_t bstrFullNameWrap(bstrFullName, false);
std::cout << "Assembly name is: " << bstrFullNameWrap << "\n";
}
Every attempt of getting the factory to return to me an interface to cppns::cppInterface within this secondary application domain has failed. I have even attempted to create a secondary factory that is a C# class that returns the pointer to the implemented interface so that an Invoke call on the Assembly would hopefully cause the rest of the code to execute in the AppDomain that I loaded the Assembly into but the Invoke returns an IDispatch pointer that I can't seem to map back into any type of C++ pointer on my interface.
namespace cppns
{
public ref class NetFactory
{
public:
NetFactory()
{
}
cppInterface *CreateInterop()
{
return GetImplObject();;
}
};
}
Is there another way to get everything to run in a secondary AppDomain or is the IDispatch pointer usable in calling the Start method?
I have managed to get most of the .NET stuff running in another domain. It seems like there is no way to get the CLI layer to run in anything other than the default AppDomain.
To make this work I needed to make the class that sits within both appdomains derive from MarshalByRefObject. In my example above that meant I had to change MyInternalCSharpClass so that it derived from MarshalByRefObject. It was also nessary to made the objects sent and returned from MyInternalCSharpClass also derive from MarshalByRefObject. Finally I these same objects that were passed and returned had to have the [Serializable] property and to also mark all their private variables public. Note if the classes being transferred though the AppDomains are already using the Serializable attribute you can use [XmlIgnore] on each formally private variable to avoid changing the serialization that is being done.
Now that everything can be moved between the AppDomains I created a second AppDomain by doing the following:
bool CreateInstanceInAppDomain(const char *pcAppDomainName)
{
bool bRtn = false;
gcroot<String^> csStrAppDomainName (gcnew String(pcAppDomainName));
mAppDomain = AppDomain::CreateDomain(csStrAppDomainName);
delete csStrAppDomainName;
Object^ MyInternalObject = mAppDomain->CreateInstanceAndUnwrap("AssemblyName", "ClassNameSpace.MyInternalCSharpClass");
mInternalClassAccess = dynamic_cast<MyInternalCSharpClass^>(MyInternalObject);
if (mInternalClassAccess)
{
bRtn = true;
}
return bRtn;
}

Com Initialization Error 0x80040154 loading c# COM Object with c++ Program

I'm working on my first COM project that is importing a c# DLL with a C# COM Wrapper class into a C++ native code application. Our application is based on the CSRegFreeCOMServer VS2008 sample project from Microsoft's All-In-One Framework. Our system is using - VS2008, .Net3.5, boost 1.4.2 and Qt 4.6.2.
This application has been running fine on our 32bit XP dev boxes. However, when we load the system onto our Windows 7-64bit system. We cannot get the com objects to initialize. We keep getting error 0x80040154 (which I cannot seem to determine what it means).
Our header file is -
#ifndef ControlComInterface_h__
#define ControlComInterface_h__
#include <string>
#include <ole2.h> // OLE2 Definitions
// Importing mscorlib.tlb is necessary for .NET components
// see:
// http://msdn.microsoft.com/en-us/library/s5628ssw.aspx
#import "mscorlib.tlb" raw_interfaces_only \
high_property_prefixes("_get","_put","_putref") \
rename("ReportEvent", "InteropServices_ReportEvent")
using namespace mscorlib;
// import the COM Declarations exported com the CSRegFreeCOMServer
#import "..\CSRegFreeCOMServer\bin\Release\CSRegFreeCOMServer.tlb" no_namespace named_guids
using namespace std;
class ControlComInterface
{
public:
ControlComInterface(void);
~ControlComInterface(void);
IFieldsPtr spFields;
IPchFilePtr spPchFileWrapper;
bool CreateInterfaceObjects(string &errorMsg);
};
#endif // ControlComInterface_h__
The simplified class code is
#include "ControlComInterface.h"
#include <boost/lexical_cast.hpp>
ControlComInterface::ControlComInterface(void)
{ }
ControlComInterface::~ControlComInterface(void)
{ }
bool ControlComInterface::CreateInterfaceObjects( string &errorMsg )
{
HRESULT hr = S_OK;
hr = ::CoInitialize(NULL);
if (FAILED(hr))
{
errorMsg = "CoInitialize failed w/err: ";
errorMsg.append(boost::lexical_cast<string>(hr));
return false;
}
errorMsg = "";
hr = spFields.CreateInstance(__uuidof(Fields));
if (FAILED(hr))
{
errorMsg = "IFields::CreateInstance failed w/err: ";
errorMsg.append(boost::lexical_cast<string>(hr));
return false;
}
return true;
}
The code is failing with a error code of 0x80040154 on the call to spFields.CreateInstance(...), which just creates an instance of the class in the com object using a default constructor.
Suggestions?
0x80040154 is REGDB_E_CLASSNOTREG. That is, class not registered.
The COM couldn't find (in the registry) the class factory with CLSID = __uuidof(Fields).

Categories

Resources