I have a legacy MMC application that uses JavaScript handlers from an Object loaded by CLSID from a C++ DLL.
Something like:
<script>
if (Control) {
Control.doStuff();
} else {
// log error
}
</script>
<Object id=Control classid="CLSID..."/>
Do I undestand it correctly, that it is enough to define a COM interface (via IDL, for example) with methods in order to access a C++ COM object and call its methods?
What types should I use within a method signature to be able to safely call it from IE JavaScript?
Related
I have a com dll that gets used in a c++ project. I have not registered the dll but its in the same solution. My application continuously breaks and I found that it breaks here
hr = CoCreateInstance(rclsid, pOuter, dwClsContext, __uuidof(IUnknown), reinterpret_cast<void**>(&pIUnknown));
because I don't have a com class with that guid (rclsid). I did more digging and I found that it gets the guid from a .tlh file in a temp directory but the guid differs from my code.
.tlh file
struct __declspec(uuid("b10a18c8-7109-3f48-94d9-e48b526fc928"))
DocMapEntry;
// [ default ] interface _DocMapEntry
// interface _Object
// interface IDocMapEntry
c# code (com visable dll)
[ComVisible(true)]
[Guid("E98676B9-1965-4248-A9B6-74EC5EE5385A")]
[InterfaceType(ComInterfaceType.InterfaceIsDual)]
public interface IDocMapEntry
{
int Offset { get; set; }
int PageOffset { get; set; }
int PageNumber { get; set; }
}
I tried changing the value to the correct value but it continuously gets changed and visual studio asks me to reload.
When I look at the class using com explorer I get a different clsid, and when I try to unregister it, it completes successfully but the item is still there and the file location is mscoree.
my c++ application uses the guid in the .tlh file but doesn't find the file.
public class DocMapEntry : IDocMapEntry
{
...
}
My questions are.
Do I need to register my com dll for this to work?
how can I set the guid for the class?
Why would it create a temp .tlh file with the wrong guid? (optional)
why can't I change this file? (optional)
Thank you.
public interface IDocMapEntry
COM strongly distinguishes between a CLSID and a IID. The IID, the interface identifier, identifies an interface implemented by a COM object. We can see that one, you specified it with the [Guid] attribute. You'd pass it as the 4th argument of CoCreateInstance(), the one where you now pass the IID of IUnknown.
What you need is the CLSID, the class identifier. We can't see it in your snippet, it is the one for whatever class implements your IDocMapEntry. With some odds that it is named DocMapEntry. And additional odds that you didn't give it a [Guid] so the CLR will auto-generate one for you. Note that it will change when you modify the class, one possible reason for CoCreateObject() to fail.
Forgetting to register the .NET assembly with Regasm.exe /codebase is another reason, you stated as much in your question. This is required or COM will not be able to find the DLL and fails the call with error code 0x80040154, "Class not registered". Having it in the same solution is not sufficient, COM search rules are not anything like those used by .NET.
Also note that you are exposing the internals of the class, avoid that by applying the [ClassInterface(ClassInterfaceType.None)] attribute on the class.
No you do not have to register the COM DLL but you do need to register an object responsible for creating the COM objects. Use do this in your application by calling CoRegisterClassObject after the COM subsystem has been initialized (by calling CoInitialize or CoInitializeEx).
IClassFactory *factory = new ObjectFactory();
// Register the factory responsible for creating our COM objects
DWORD classToken;
HRESULT hr = CoRegisterClassObject(
Object_CLSIID,
factory,
CLSCTX_LOCAL_SERVER,
REGCLS_MULTIPLEUSE,
&classToken);
// Now we can create the objects
Object *obj = nullptr;
hr = CoCreateInstance(
Object_CLSIID,
nullptr,
CLSCTX_LOCAL_SERVER,
Object_IID,
reinterpret_cast<void**>(&obj));
Not sure why it's creating a temporary .tlh but I suspect it's something in your build settings - possibly in the build steps.
Is your COM object in a C# dll ? If yes, did you run regasm previously in your system, which could have registered the COM dll.
Guid("E98676B9-1965-4248-A9B6-74EC5EE5385A"): This is your interface GUID, not your component guid.
You should have a GUID declared for COM object in the implementation class, which should be b10a18c8-7109-3f48-94d9-e48b526fc928.
Why would it create a temp .tlh file with the wrong guid?
Are you sure you have not imported the COM dll using #import keyword.
Stack Overflow
Hi,
I'm writing a C# application to sniff the network traffic for an application written with Qt. As best as I can tell,the target application uses:
class QByteArray _thiscall QIODevice::read(_int64)
to read the data. My C# application uses the EasyHook library to intercept any calls to QIODevice::read(), and then passes the parameters off to a C++/CLI object that tries to call the native method. The native method returns a QByteArray, and I pass this back to C#, which passes it back to the target application.
In summary:
Target App => Easyhook/C# => C++/CLI => Native Method => C++/CLI => Easyhook/C# => Target App
The architecture seems to work perfectly EXCEPT when the return type of the intercepted method is a native object (such as a QByteArray above).
My C# code looks like this:
// //////////////////////////////////
// public: class QByteArray __thiscall QIODevice::read(__int64)
// //////////////////////////////////
[UnmanagedFunctionPointer(CallingConvention.ThisCall, CharSet = CharSet.Unicode, SetLastError = true)]
delegate IntPtr DDeviceRead(IntPtr Device, Int64 maxSize);
// Intercept all calls to Device::Read()
public IntPtr DeviceRead_Intercepted(IntPtr Device, Int64 maxSize)
{
// Call the method from C++/CLI
IODeviceWrapper qtDevice = new IODeviceWrapper(Device);
return qtDevice.Read(maxSize);
}
And my C++/CLI code looks like this:
IntPtr IODeviceWrapper::Read(Int64 maxSize)
{
QByteArray * retArray = &Native->read(maxSize);
return static_cast<IntPtr>(retArray);
}
C++ isn't my favoured language - so things like pointers and references to objects can be a little confusing at times. I've tried a whole lot of variations, but can't seem to make sense of it.
The native Qt read() function I assume returns the address of a newly allocated QByteArray, which the variable accepts, so my C++/CLI code needs to pass that address all the way along the chain back to the target app.
How is this done?
I'm trying to create a dll that contains a VCL data module - the idea being that various applications can all load the same dll and use the same database code.
The data module itself is tested ok as part of an application - I've copied the form over to my dll project.
So in the dll entry point method, I need to initialize the data module:
int WINAPI DllEntryPoint(HINSTANCE hinst, unsigned long reason, void* lpReserved)
{
//if I don't call this, I get an exception on initializing the data module
CoInitialize(NULL);
//initialize a standard VCL form; seems to works fine
//I'm not using Application->CreateForm as I don't want the form to appear straight away
if(!MyForm) MyForm = new TMyForm(Application);
//this doesn't work - the thread seems to hang in the TDataModule base constructor?
//I've also tried Application->CreateForm; same result
if(!MyDataModule) MyDataModule = new TMyDataModule(Application);
}
I've also seen something about how I need to call Application->Initialize before creating the form but this doesn't seem to make any difference.
Any ideas?
Thanks
You really should not be doing very much work in your DllEntryPoint() at all. Certainly not calling CoInitialize(), anyway. It is not the DLL's responsibility to call that when loaded. It is the calling app's responsibility before loading the DLL.
You should either:
export an additional function to initialize your DLL and then have the app it after loading the DLL (same for uninitialing the DLL before unloading it)
don't create your TForm/TDataModule until the first time the DLL actually needs them.
move your TForm/TDataModule into their own worker thread inside the DLL. In this case, you would then call CoIniitalize().
And in all cases, don't relay on the DLL's Application object to manage the lifetime of your TForm/TDataModule. Free them yourself instead before the DLL is unloaded.
I have a C++ dll which is a plug-in to ADOBE Acrobat. It needs to talk often ( to and fro) and with a fair amount of complicated data-structures to a WPF process.
Any thoughts of what might be the best way to go . Need something that is a little long term and maintainable, in other words would love some ideas around something that lets both process make what looks like methods calls and some infrastructure piece does the marshaling and dispatch . I've tried Windows messages but ran into some conflict issues on ADOBE, also not really interested in anything that causes the dll to get adobe to load the CLR. Only other things that come to my mind are named pipes or http.
Thanks in Advance
Named pipes could do but you won't get a feeling of just calling functions. Named pipe are quite low-level IPC. Other IPC options are:
Windows RPC, you definitely get a feeling of just calling functions.
What about hosting a COM object in WPF application and calling it from the Adobe plugin?
I would go with COM:
Implement an interface in WPF app
generate a typelib (e.g by using regasm)
import the typelib into C++ dll
communicate
if you need bidirectional communication, C++ dll can also implement a COM interface which is then accessed from WPF application.
This is what I have used to connect legacy C++ app with new .NET service, and it works great. The biggest issue is to find people who know COM, but fortunately this doesn't require a deep understanding of COM.
your hint with COM is very intersting. I tried to implement this concept.
I have created an interface in my WPF CallDllFromWpf3Interface project:
using System.Runtime.InteropServices;
namespace CallDllFromWpf3Interface
{
[Guid("F6E0E2E8-CCC6-487B-8BF1-261265061E6A")]
public interface SetValueInterface
{
void SetValue(int value);
}
}
Then I have generated the typelib with the regasm tool:
regasm CallDllFromWpf3Interface.exe /tlb
With the "oleview" tool I can see the typelib and the interface.
The next step was to create a c++ dll project called "CallSetValueInterface".
In my CallSetValueInterface.cpp file I wrote this lines:
#import "D:\Thomas\Programming\WPF\Basics\CallDllFromWpf\CallDllFromWpf3Interface\CallDllFromWpf3Interface\bin\Debug\CallDllFromWpf3Interface.tlb"
void
CallSetValueInterface::startAcq(void)
{
CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
CallDllFromWpf3Interface::SetValueInterfacePtr Svip("f6e0e2e8-ccc6-487b-8bf1-261265061e6a");
Svip->SetValue(55);
Svip = NULL;
CoUninitialize();
}
After a successful build of the dll project I copied "CallSetValueInterface.dll" to the "CallDllFromWpf3Interface" project.
Finally I changed my WPF code to:
#region SetValueInterface Members
public void SetValue(int value)
{
MyValue = value;
}
#endregion
[DllImport("CallSetValueInterface.dll", EntryPoint = "startAcq", ExactSpelling = true, SetLastError = true)]
public static extern void StartAcqFromDll();
private void Button_Click(object sender, RoutedEventArgs e)
{
StartAcqFromDll();
}
And when the debugger came to StartAcqFromDll() there occurred an error dialog "An unhandled exception of type 'System.Runtime.InteropServices.SEHException' occurred in CallDllFromWpf3Interface.exe".
Does anybody know whats going wrong?
Regards,
ThomasL.
I'm trying to build an extension for Firefox. This extension uses an XPCOM component (a C++ dll). I'm compiling the DLL, compilation is OK.
I also succeeded in building a JS code which instanciates the object from XPCOM:
try {
greenfox;
return true;
} catch( e ) {
alert( e );
return false;
}
The object returned is this one:
QueryInterface
QueryInterface()
__proto__
[xpconnect wrapped native prototype] { QueryInterface=QueryInterface()}
QueryInterface
QueryInterface()
Everything is fine, except I can't call the function which are supposed to be in my XPCOM component.
Here is my IDL file:
[scriptable, uuid(ec8030f7-c20a-464f-9b0e-13a3a9e97384)]
interface nsISample : nsISupports
{
attribute string value;
void writeValue(in string aPrefix);
void poke(in string aValue);
void start();
double stop();
};
When callingstart() function, I get the Javascript error: "is not a function"
greenfox.start();
Do you have any idea? It seems like no function is exposed in my XPCOM.
You seem to be looking at an object exposing only the nsISupports interface. Your interface (nsISample) won't get exposed by default, you have to explicitly request it. You can do it for example by instantiating your component like this:
var greenfox = Components.classes["..."].getService(Components.interfaces.nsISample);
greenfox.start();
Alternatively, you can also call QueryInterface on an object you already have:
greenfox.QueryInterface(Components.interfaces.nsISample);
greenfox.start();
Generally, I wouldn't recommend using a binary XPCOM component for reasons outlined here, maintaining them requires way too much effort. I would rather suggest compiling a regular DLL and using it via js-ctypes. Reference a binary-component to js-ctypes mentions how you would locate a DLL inside your add-on to use it via js-ctypes.
Do you call QueryInterface with your uuid? It is necessary to call it before using the component instance created. Does your usage match what's in here?
If you don't want to deal with XPCOM you can use js-ctypes.