I'm building a VC++ dll that exposes the functionality of some Hardware by its driver dll which uses COM Objects. The driver object (COMDriver) is a _com_ptr_t which encapsulates the interface that has all the hardware functions. My VC++ code exposes its functions using JNI to be used by a Java application upper in the stack, but it has been a pain managing the objects lifetime using COM and JNI together, as I'm not the most experienced COM guy...
In the Java client, I have up to 3 threads that handle the communication to each own device. They call the VC++ dll in order to send the commands to each device.
On the VC++ dll, I want to be able to create several instances of the driver _com_ptr_t via a JNI call (let's call it openConnection) and store them in a global map that matches the JNIEnv pointer address (that maps out to the calling Java thread) to the respective driver instance (the _com_ptr_t instance), something like this:
//Maps the caller Java Thread (by JNIEnv*) to the respective driver instance.
unordered_map<uintptr_t, ICOMDriverPtr> stateMap;
JNIEXPORT jboolean JNICALL Java_openConnection(JNIEnv *env, jobject jobj){
//This pointer is a reference to the Handler Thread that calls openConnection
uintptr_t env_addresss = reinterpret_cast<uintptr_t>(env);
::CoInitialize(NULL);
try
{
ICOMDriverPtr spDriver;
// Create an instance of the driver.
spDriver.CreateInstance(__uuidof(COMDriver));
//do driver stuff....
//store the mapping of JNIEnv and the driver COM smart pointer
stateMap[env_addresss] = spDriver;
}
catch { //error handling
When I make a call to this driver after initializing it via another JNI call I was doing ICOMDriverPtr spDriver = stateMap[env_addresss]; but I have a COM error saying Invalid pointer to this newly created spDriver.
I tried working around GetInterfacePtr and tried multiple & and * operators with problems on compilation. in runtime this is what worked but still can't get a valid reference to the previously created spDriver.
From the Microsoft documentation on the _com_ptr_t class, the = operator should create a copy of the smart pointer stored in the map (on the second call). Should I detach the smart pointer on the OpenConnection call? (because I think it might be releasing the pointer after the JNI scope ends on the open connection, but I'm not sure). If someone could shed a light on how to copy and reference the COMDriver around JNI I would be much grateful, Thank you!
Related
I’m trying to store an object passed from JavaScript to a Node.js Addon in a void *. I can’t seem to get this to compile; building with node-gyp produces error: no matching function for call to 'Cast'.
The long version of what I’m trying to do is write a Node.js Addon that runs Csound. Csound works, from a bird’s-eye view, with C functions that take a pointer to an opaque Csound struct as (usually) the first argument. This struct contains a void * to “hostData”, arbitrary data set by a program hosting Csound. Some things that Csound does, like posting messages, are modified with callbacks—function pointers in this case. I need a place to store callbacks for each instance of Csound, so I’m trying to let someone set hostData to an object from JavaScript, but I also want to set the callbacks for a Csound instance as hidden properties on this hostData object.
I think the code will need to look something like
#include "csound.h"
#include <node.h>
static void CsoundMessageCallback(CSOUND *Csound, int attributes,
const char *format, va_list valist)
{
// Call the JavaScript function we stored in the hostData of Csound.
}
static void _wrap_csoundSetMessageCallback(
const v8::FunctionCallbackInfo<v8::Value>& args)
{
v8::HandleScope scope(v8::Isolate::GetCurrent());
CSOUND *Csound;
// Pretend we get the Csound instance from args[0] here. This is actually done
// by SWIG <http://www.swig.org>.
// This does not compile. csoundGetHostData() returns a void *, but I’m assuming
// hostData was set to an object from JavaScript.
v8::Persistent<v8::Object> hostData =
v8::Persistent<v8::Object>::Cast(csoundGetHostData(Csound));
hostData.SetHiddenValue(
v8::String::New("CsoundMessageCallback"),
v8::Persistent<v8::Function>::Cast(args[1])
);
csoundSetMessageCallback(Csound, CsoundMessageCallback);
}
I’m guessing I need to take a close look at V8’s internal fields, but I’m really not sure.
Typically what I've done in situations like this is I write a wrapper C++ class (inheriting from node's ObjectWrap class) that stores a pointer to the instance of whatever C/C++ class I'm wrapping and has various public methods to interact with that instance.
When new is called from JS land, a new instance of the wrapper C++ class gets created and associated with the new JS object. Then you have JS functions that kick off whatever async tasks that utilize the wrapped library's callbacks.
From there it's just a matter of calling uv_async_send() from the wrapped library's callbacks to signal the main thread and then calling the JS callback from the uv_async callback.
You can see an example of all of this here (especially in the Windows-specific parts):
The Pcap class holds a pcap_t pointer (would be a CSOUND pointer for you).
When a new Pcap is created from JS land, I wrap a new C++ class instance.
Initialize a uv_async_t which sets up the callback to fire on uv_async_send() and also associates the user data pointer to the class instance for easy access. You could do this initialization during the call to new if you wanted, instead of a separate prototype function (open()) like I have done since initialization just happens once.
Then from the wrapped library's callback, I signal the main thread.
From the uv_async callback, I can then access the wrapper class instance and use V8 functions safely. Although in my particular case, I have another callback which uses V8 functions. However you can use them safely inside your uv_async callback.
As far as storing JS callbacks goes, there are different ways to handle that. One solution might be to create a baton object that stores a Persistent copy of the JS callback and the wrapper class instance and store that baton in uv_async_t's user data pointer. This would mean creating a new uv_async_t for every request (which is different than the example I gave above).
i have searched all over the world knowing that, we should DeleteLocalRef if it's created in JNI code
then, should i also delete it if the object is newed and returned by Java code? such as:
// in java code
public SomeObject funcInJavaCode() {
return new SomeObject();
}
// in jni code
funcInJNI {
jobject obj = env->CallObjectMethod(...);
...
// do i have to delete the obj here???
env->DeleteLocalRef(obj);
}
thanks
No. Local references are garbage collected when the native function returns to Java (when Java calls native) or when the calling thread is detached from the JVM (in native calls Java). You need explicit DeleteLocalRef only when you have a long lived native function (e.g., a main loop) or create a large number of transient objects in a loop.
You definitely can NOT delete the local ref to a returned object since that call will free up the reference to the object. For example
jbitmap = invokeObjectJavaMethod("MFImageToNative", "([B)Landroid/graphics/Bitmap;", byte_array);
env->DeleteLocalRef(jbitmap);
return jbitmap;
will crash, I believe that it the consumer of the method's responsibility to deal with freeing up the reference. I some kind soul could provide clarification on how to do this, I would be most grateful.
I have a out-of-proc COM server which has two ATL COM objects . object1 exposes an interface which internally creates object2 and returns an interface pointer to it in following way:
HRESULT CObject1::CreateObject2(IObject2** pIobj2)
{
CComObject<Object2>* pObj = NULL;
HRESULT hr = CComObject<Object2>::CreateInstance(&pObj);
hr = pObj->QueryInterface(IID_IObject2,(void**)pIobj2);
//Some reason i need to store this pIobj2
(*pIobj2)->AddRef();
return hr;
}
Object1 exposes another interface to remove object2
HRESULT CObject1::RemoveObject2(IObject2* pIobj2)
{
pIobj2->Release(); //This to compensate QI done in CreateObject2
pIobj2->Release(); //This to compensate addref done to store in create
}
My requirement is that the client should not call Release on the IObject2 pointer anywhere after IObject1::CreateObject2(). To destroy object2 it should call IObject1::RemoveObject2().When I execute the client, after RemoveObject2, object2 is not destroyed, however when I call release on the Iobject2 pointer in client after RemoveObject2 , the object2 gets destroyed.
Life cycle management in out of process COM object is much more complex than in-proc. For example, the system has to take into account that the other party may die (for example, the server should clean up resources allocated for an object, if the client process has died without a Release). For this Microsoft has a DCOM garbage collection.
There are indeed two COM objects, on the client (proxy) and on the server (stub). The proxy maintains its own reference count, and your code confuses this reference count, since you release a reference count of object 2 on the server - information that will not get to the client.
I strongly encourage you to change your architecture. For one, the COM contract is that the way to release object is with the Release method. That's the contract, and your design changes it, and this is why you get DCOM confused.
I think a better design would be:
In Object2 keep a reference to Object1 (say, m_pobj1)
Do NOT have Object1::RemoveObject2. Instead have a private (non COM) CleanupObject2, if you need to do some work.
Implement Object2::FinalRelease (the cleanup method of Object2), and call m_pobj1->Cleanup[Object2(this)
Remember, that if you need to keep pointers to obj2 within obj1, make sure these are weak references. That is - just pointers - without adding a reference count.
Clients call Object1::CreateObject2 and get a reference to obj2. When they are done, they will call obj2->Release(), and will clear the reference count, and call obj1->CleanupObject2 method.
I have a COM class CMyCOMServer implementing IMyInterface in one application, both with correct GUIDs. CMyCOMServer::QueryInterface will return S_OK (and cast itself to the right type) if IUnknown or IMyInterface is requested, otherwise it returns E_NOINTERFACE.
In another app on the same PC, I call:
HRESULT hr = ::CoCreateInstance(__uuidof(CMyCOMServer), 0, CLSCTX_SERVER,
__uuidof(IMyInterface ),(void **)&pInterface);
It returns E_NOINTERFACE. So I assumed I was doing something wrong and added a breakpoint on CMyCOMServer::QueryInterface. I found that when CoCreateInstance is called, QueryInterface is triggered several times for different interfaces:
First, IUnknown is requested - no problem
Then, several interfaces like IMarshall etc are requested... these are not supported so E_NOINTERFACE is returned
Finally, IMyInterface is requested. I verify QueryInterface returns S_OK and sets (IMyInterface *)this as the interface pointer, as expected
So my confusion is why the calling CoCreateInstance is leaving me a NULL pointer and return code of E_NOINTERFACE, when the COM server app is clearly returning the interface I ask for?
EDIT: my client app calls CoInitialize(NULL) at startup, this makes no difference.
If your COM server is running in a different process, or a different apartment in the same process, COM needs to know how to package and transmit parameters when you make calls to your interface. This process is called "marshaling".
If you define a custom interface, you need to implement marshaling for it using one of the following approaches.
Standard marshaling: have the MIDL compiler to generate a proxy
and stub which you must register on the system. This is probably the best option since you have already defined your interface.
OLE Automation marshaling: you define an automation compatible
custom interface and use the
marshaller which is already part of
the COM framework
Custom marshaling: you implement the methods of IMarshal
When you are debugging your COM server, although you see that you are returning your custom interface in the call to QueryInterface, it does not make it across the process boundary because COM cannot figure out how to marshal that interface, hence the client sees E_NOINTERFACE.
UPDATE (based on your comment)
If this is an existing COM server app then you probably already have a proxy/stub. You need to register this on both the client and server. Could it be that you were testing this on a new machine(s) and you simply forgot to register this? To register you simply do regsvr32 on the proxy/stub dll.
This happens because COM subsystem tries to marshal your custom interface (IMyInterface) and simply has no idea how to do that. That happens either because the server is out-proc or because the server is in-proc and the thread of the consumer application that calls CoCreateInstance() has called CoInitialize()/ CoInitializeEx() incorrectly so that "multithreaded apartment" is requested as mentioned in the article user Thomas refers to in the other answer.
If you only need an in-proc server you could suppress marshalling by ensuring that the thread calling CoCreateInstance() either calls CoInitialize() or CoInitializeEx() with COINIT_APARTMENTTHREADED to enforce "single-threaded apartment".
If you need an out-proc server you can't get around marshalling. In the latter case you could do one of the following:
implement IMarshal - least preferable
add proxy/stubs and register them for your custom interface
(not sure if it will work for out-proc, but it's the simplest) if your interface can be marshalled with automation marshaller simply include a typlib into the resources of your COM server and register that typelib in the registry.
Could this be the threading model problem that Raymond Chen wrote about?
Edit in reply to the comment:
If your threading model is incompatible with the threading model of the object you're creating, then COM marshalling kicks in. And if the marshalling stuff isn't there, the error that comes out is E_NOINTERFACE, because the marshalling interface is missing.
It's more about threading models than about marshalling, really.
The previous comments about E_NOINTERFACE returned because marshalling interface is missing was very helpful,
however,
for us the answer/fix was to force the main application (the one calling CoCreateInstance) to be STA (single threaded apartment), and this was done by setting an advanced linker option, i.e.:
"CLR Thread Attribute" is set to "STA threading attribute"
or on the link command line you do:
"/CLRTHREADATTRIBUTE:STA"
This prevents a mix of MTA and STA, which causes a call across threads.
Hope someone else finds this helpful.
currently I'm struggling trying to use a COM dll on a simple system that I've made. Everything compiles successfully, but in runtime the CoCreateInstace is returning S_OK, but somehow my object pointer is returning NULL.
This interface pointer is created on my class header. The weirdest thing is that instantiating this same pointer type on the method stack results in a correct object, but subsequent calls to __hook turn on an access violation when trying to create a BASE com class.
Some other aspects that might be useful:
Tried to run the program with CoInitalizeEx started as COINIT_MULTITHREADED and COINIT_APARTMENTTHREADED
The project is a dll which uses the COM dll in it
I've tried the same method without starting a new thread and the error persists
I've made a test program ( no threads, executable ) and the object is created normally, and hooked correctly. So my guess it is something related to it being a DLL itself or threaded related.
PS: As bonus question, why google doesn't return anything favorable related to COM? :)
It sounds like a bug in the COM object's implementation of IUnknown::QueryInterface - not setting the output pointer but returning S_OK.
CoCreateInstance for an in-proc server is basically:
Load the DLL into memory
Call DllGetClassObject to get the class factory
Call IClassFactory::CreateInstance from the class factory which allocates a new object
Call IUnknown::QueryInterface on the new object to get the desired interface.
Returning NULL but S_OK at any step should result in a crash, except for the QI call at the end.
Found the problem: The module attribute was defined on a static library, and that made the COM object go crazy; Moving it to the DLL source resolved the problem.