Files
jlibcom/jni/EventProxy.cpp
clay_shooter 1e69bb9acb SF 3435567
Possibility to pass a NULL Dispatch pointer
Fix a memory leak (detected with Glowcode) in Variant.cpp/zeroVariant function
Variant.toString improvement to handle NULL cases
Adds the error code to the message when "an unknown COM error has occurred"
Added debug info to EventProxy advise registration failure message.
2011-12-11 16:57:28 +00:00

877 lines
18 KiB
C++

/*
* Copyright (c) 1999-2004 Sourceforge JACOB Project.
* All rights reserved. Originator: Dan Adler (http://danadler.com).
* Get more information about JACOB at http://sourceforge.net/projects/jacob-project
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "EventProxy.h"
#include "Variant.h"
// hook myself up as a listener for delegate
EventProxy::EventProxy(JNIEnv *env,
jobject aSinkObj,
CComPtr<IConnectionPoint> pConn,
IID eid,
CComBSTR mName[],
DISPID mID[],
int mNum) :
// initialize some variables
m_cRef(0), pCP(pConn),
eventIID(eid), MethNum(mNum), MethName(mName),
MethID(mID)
{
// keep a pointer to the sink
javaSinkObj = env->NewGlobalRef(aSinkObj);
if (env->ExceptionOccurred()) { env->ExceptionDescribe(); env->ExceptionClear();}
// we need this to attach to the event invocation thread
env->GetJavaVM(&jvm);
if (env->ExceptionOccurred()) { env->ExceptionDescribe(); env->ExceptionClear();}
AddRef();
Connect(env);
}
void EventProxy::Connect(JNIEnv *env) {
HRESULT hr = pCP->Advise(this, &dwEventCookie);
if (SUCCEEDED(hr)) {
connected = 1;
} else {
connected = 0;
// SF 3435567 added debug info to advise failed message
char tmp[256];
sprintf_s( tmp, 256, "Advise failed with %x (CONNECT_E_ADVISELIMIT is %x)", (int)hr, (int)(CONNECT_E_ADVISELIMIT) );
ThrowComFail(env, tmp, hr);
}
}
// unhook myself up as a listener and get rid of delegate
EventProxy::~EventProxy()
{
JNIEnv *env;
Disconnect();
jint vmConnectionStatus = JNI_EVERSION ;
jint attachReturnStatus = -1; // AttachCurrentThread return status.. negative numbers are failure return codes.
// attach to the current running thread -- JDK 1.4 jni.h has two param cover for 3 param call
vmConnectionStatus = jvm->GetEnv((void **)&env, JNI_VERSION_1_2);
if ((env != NULL)&& env->ExceptionOccurred()) { env->ExceptionDescribe(); env->ExceptionClear();}
if (vmConnectionStatus == JNI_EDETACHED){
//printf("Unhook: Attaching to current thread using JNI Version 1.2 (%d)\n",vmConnectionStatus);
JavaVMAttachArgs attachmentArgs;
attachmentArgs.version = JNI_VERSION_1_2;
attachmentArgs.name = NULL;
attachmentArgs.group = NULL;
attachReturnStatus = jvm->AttachCurrentThread((void **)&env, &attachmentArgs);
if ((env != NULL) && env->ExceptionOccurred()) { env->ExceptionDescribe(); env->ExceptionClear();}
} else {
// should really look for JNI_OK versus an error because it could have been JNI_EVERSION
// started method with thread hooked to VM so no need to attach again
//printf("Unhook: No need to attach because already attached %d\n",vmConnectionStatus);
}
// we should always have an env by this point but lets be paranoid and check
if (env != NULL){
env->DeleteGlobalRef(javaSinkObj);
if (env->ExceptionOccurred()) { env->ExceptionDescribe(); env->ExceptionClear();}
}
if (MethNum) {
delete [] MethName;
delete [] MethID;
}
// detach from thread only if we attached to it in this function
if (attachReturnStatus == 0){
jvm->DetachCurrentThread();
//printf("Unhook: Detached\n");
} else {
//printf("Unhook: No need to detatch because attached prior to method\n");
}
//fflush(stdout);
}
void EventProxy::Disconnect() {
if (connected) {
// insure we don't call Unadvise twice
connected = 0;
pCP->Unadvise(dwEventCookie);
}
}
// I only support the eventIID interface which was passed in
// by the DispatchEvent wrapper who looked it up as the
// source object's default source interface
STDMETHODIMP EventProxy::QueryInterface(REFIID rid, void **ppv)
{
if (rid == IID_IUnknown || rid == eventIID || rid == IID_IDispatch)
{
*ppv = this;
AddRef();
return S_OK;
}
return E_NOINTERFACE;
}
// This should never get called - the event source fires events
// by dispid's, not by name
STDMETHODIMP EventProxy::GetIDsOfNames(REFIID riid,
OLECHAR **rgszNames, UINT cNames, LCID lcid, DISPID *rgDispID)
{
return E_UNEXPECTED;
}
// The actual callback from the connection point arrives here
STDMETHODIMP EventProxy::Invoke(DISPID dispID, REFIID riid,
LCID lcid, unsigned short wFlags, DISPPARAMS *pDispParams,
VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr)
{
const char *eventMethodName = NULL; //Sourceforge report 1394001
JNIEnv *env = NULL;
// map dispID to jmethodID
for(int i=0;i<MethNum;i++)
{
if (MethID[i] == dispID) {
USES_CONVERSION;
eventMethodName = W2A((OLECHAR *)MethName[i]);
}
}
// added 1.12
if (!eventMethodName) {
// just bail if can't find signature. no need to attach
// printf("Invoke: didn't find method name for dispatch id %d\n",dispID);
return S_OK;
}
if (DISPATCH_METHOD & wFlags)
{
// attach to the current running thread
//printf("Invoke: Attaching to current thread using JNI Version 1.2\n");
JavaVMAttachArgs attachmentArgs;
attachmentArgs.version = JNI_VERSION_1_2;
attachmentArgs.name = NULL;
attachmentArgs.group = NULL;
jvm->AttachCurrentThread((void **)&env, &attachmentArgs);
if (env->ExceptionOccurred()) { env->ExceptionDescribe(); env->ExceptionClear();}
if (!eventMethodName)
{
// could not find this signature in list
// printf("Invoke: didn't find method name for dispatch id %d\n",dispID);
// this probably leaves a native thread attached to the vm when we don't want it
ThrowComFail(env, "Event method received was not defined as part of callback interface", -1);
// should we detatch before returning?? We probably never get here if we ThrowComFail()
// jvm->DetachCurrentThread();
return S_OK;
}
// find the class of the InvocationHandler
jclass javaSinkClass = env->GetObjectClass(javaSinkObj);
if (env->ExceptionOccurred()) { env->ExceptionDescribe(); env->ExceptionClear();}
//printf("Invoke: Got sink class\n");
jmethodID invokeMethod;
invokeMethod = env->GetMethodID(javaSinkClass, "invoke", "(Ljava/lang/String;[Lcom/jacob/com/Variant;)Lcom/jacob/com/Variant;");
if (env->ExceptionOccurred()) { env->ExceptionDescribe(); env->ExceptionClear();}
jstring eventMethodNameAsString = env->NewStringUTF(eventMethodName);
//printf("Invoke: Got method name\n");
// now do what we need for the variant
jmethodID getVariantMethod = env->GetMethodID(javaSinkClass, "getVariant", "()Lcom/jacob/com/Variant;");
if (env->ExceptionOccurred()) { env->ExceptionDescribe(); env->ExceptionClear();}
//printf("Invoke: Found way too getVariant\n");
jobject aVariantObj = env->CallObjectMethod(javaSinkObj, getVariantMethod);
if (env->ExceptionOccurred()) { env->ExceptionDescribe(); env->ExceptionClear();}
//printf("Invoke: Made Variant\n");
jclass variantClass = env->GetObjectClass(aVariantObj);
if (env->ExceptionOccurred()) { env->ExceptionDescribe(); env->ExceptionClear();}
// create the variant parameter array
// how many params
int numVariantParams = pDispParams->cArgs;
// make an array of them
jobjectArray varr = env->NewObjectArray(numVariantParams, variantClass, 0);
if (env->ExceptionOccurred()) { env->ExceptionDescribe(); env->ExceptionClear();}
//printf("Invoke: Created Array\n");
int i,j;
for(i=numVariantParams-1,j=0;i>=0;i--,j++)
{
// construct a java variant holder
jobject arg = env->CallObjectMethod(javaSinkObj, getVariantMethod);
if (env->ExceptionOccurred()) { env->ExceptionDescribe(); env->ExceptionClear();}
// get the empty variant from it
VARIANT *va = extractVariant(env, arg);
// copy the value
VariantCopy(va, &pDispParams->rgvarg[i]);
// put it in the array
env->SetObjectArrayElement(varr, j, arg);
env->DeleteLocalRef(arg);
if (env->ExceptionOccurred()) { env->ExceptionDescribe(); env->ExceptionClear();}
}
//printf("Invoke: Filled Array\n");
// Set up the return value
jobject ret;
ret = env->CallObjectMethod(javaSinkObj, invokeMethod,
eventMethodNameAsString, varr);
//printf("Invoke: Invoked callback\n");
if (!env->ExceptionOccurred() && ret != NULL) {
VariantCopy(pVarResult, extractVariant(env,ret));
}
if (env->ExceptionOccurred()) { env->ExceptionDescribe(); env->ExceptionClear();}
// don't need the first variant we created to get the class
// SF 1689061 change not accepted but put in as comment for later reminder
//Java_com_jacob_com_Variant_release(env, aVariantObj);
env->DeleteLocalRef(aVariantObj);
if (env->ExceptionOccurred()) { env->ExceptionDescribe(); env->ExceptionClear();}
// Begin code from Jiffie team that copies parameters back from java to COM
for(i=numVariantParams-1,j=0;i>=0;i--,j++)
{
jobject arg = env->GetObjectArrayElement(varr, j);
VARIANT *java = extractVariant(env, arg);
VARIANT *com = &pDispParams->rgvarg[i];
convertJavaVariant(java, com);
// SF 1689061 change not accepted but put in as comment for later reminder
// note that a related fix has been submitted in SF 3435567 to do this in zeroVariant() method
//Java_com_jacob_com_Variant_release(env, arg);
zeroVariant(env, arg);
env->DeleteLocalRef(arg);
}
// End code from Jiffie team that copies parameters back from java to COM
// detach from thread
//printf("Invoke: Detatching\n");
jvm->DetachCurrentThread();
//fflush(stdout);
return S_OK;
}
return E_NOINTERFACE;
}
void EventProxy::convertJavaVariant(VARIANT *java, VARIANT *com) {
switch (com->vt)
{
case VT_DISPATCH:
{
switch (java->vt)
{
case VT_DISPATCH:
{
V_DISPATCH(com) = V_DISPATCH(java);
break;
}
case VT_DISPATCH | VT_BYREF:
{
V_DISPATCH(com) = *V_DISPATCHREF(java);
break;
}
}
break;
}
case VT_DISPATCH | VT_BYREF:
{
switch (java->vt)
{
case VT_DISPATCH:
{
*V_DISPATCHREF(com) = V_DISPATCH(java);
break;
}
case VT_DISPATCH | VT_BYREF:
{
*V_DISPATCHREF(com) = *V_DISPATCHREF(java);
break;
}
}
break;
}
case VT_BOOL:
{
switch (java->vt)
{
case VT_BOOL:
{
V_BOOL(com) = V_BOOL(java);
break;
}
case VT_BOOL | VT_BYREF:
{
V_BOOL(com) = *V_BOOLREF(java);
break;
}
}
break;
}
case VT_BOOL | VT_BYREF:
{
switch (java->vt)
{
case VT_BOOL:
{
*V_BOOLREF(com) = V_BOOL(java);
break;
}
case VT_BOOL | VT_BYREF:
{
*V_BOOLREF(com) = *V_BOOLREF(java);
break;
}
}
break;
}
case VT_UI1:
{
switch (java->vt)
{
case VT_UI1:
{
V_UI1(com) = V_UI1(java);
break;
}
case VT_UI1 | VT_BYREF:
{
V_UI1(com) = *V_UI1REF(java);
break;
}
}
break;
}
case VT_UI1 | VT_BYREF:
{
switch (java->vt)
{
case VT_UI1:
{
*V_UI1REF(com) = V_UI1(java);
break;
}
case VT_UI1 | VT_BYREF:
{
*V_UI1REF(com) = *V_UI1REF(java);
break;
}
}
break;
}
case VT_I2:
{
switch (java->vt)
{
case VT_I2:
{
V_I2(com) = V_I2(java);
break;
}
case VT_I2 | VT_BYREF:
{
V_I2(com) = *V_I2REF(java);
break;
}
}
break;
}
case VT_I2 | VT_BYREF:
{
switch (java->vt)
{
case VT_I2:
{
*V_I2REF(com) = V_I2(java);
break;
}
case VT_I2 | VT_BYREF:
{
*V_I2REF(com) = *V_I2REF(java);
break;
}
}
break;
}
case VT_I4:
{
switch (java->vt)
{
case VT_I4:
{
V_I4(com) = V_I4(java);
break;
}
case VT_I4 | VT_BYREF:
{
V_I4(com) = *V_I4REF(java);
break;
}
}
break;
}
case VT_I4 | VT_BYREF:
{
switch (java->vt)
{
case VT_I4:
{
*V_I4REF(com) = V_I4(java);
break;
}
case VT_I4 | VT_BYREF:
{
*V_I4REF(com) = *V_I4REF(java);
break;
}
}
break;
}
case VT_R4:
{
switch (java->vt)
{
case VT_R4:
{
V_R4(com) = V_R4(java);
break;
}
case VT_R4 | VT_BYREF:
{
V_R4(com) = *V_R4REF(java);
break;
}
}
break;
}
case VT_R4 | VT_BYREF:
{
switch (java->vt)
{
case VT_R4:
{
*V_R4REF(com) = V_R4(java);
break;
}
case VT_R4 | VT_BYREF:
{
*V_R4REF(com) = *V_R4REF(java);
break;
}
}
break;
}
case VT_R8:
{
switch (java->vt)
{
case VT_R8:
{
V_R8(com) = V_R8(java);
break;
}
case VT_R8 | VT_BYREF:
{
V_R8(com) = *V_R8REF(java);
break;
}
}
break;
}
case VT_R8 | VT_BYREF:
{
switch (java->vt)
{
case VT_R8:
{
*V_R8REF(com) = V_R8(java);
break;
}
case VT_R8 | VT_BYREF:
{
*V_R8REF(com) = *V_R8REF(java);
break;
}
}
break;
}
case VT_I1:
{
switch (java->vt)
{
case VT_I1:
{
V_I1(com) = V_I1(java);
break;
}
case VT_I1 | VT_BYREF:
{
V_I1(com) = *V_I1REF(java);
break;
}
}
break;
}
case VT_I1 | VT_BYREF:
{
switch (java->vt)
{
case VT_I1:
{
*V_I1REF(com) = V_I1(java);
break;
}
case VT_I1 | VT_BYREF:
{
*V_I1REF(com) = *V_I1REF(java);
break;
}
}
break;
}
case VT_UI2:
{
switch (java->vt)
{
case VT_UI2:
{
V_UI2(com) = V_UI2(java);
break;
}
case VT_UI2 | VT_BYREF:
{
V_UI2(com) = *V_UI2REF(java);
break;
}
}
break;
}
case VT_UI2 | VT_BYREF:
{
switch (java->vt)
{
case VT_UI2:
{
*V_UI2REF(com) = V_UI2(java);
break;
}
case VT_UI2 | VT_BYREF:
{
*V_UI2REF(com) = *V_UI2REF(java);
break;
}
}
break;
}
case VT_UI4:
{
switch (java->vt)
{
case VT_UI4:
{
V_UI4(com) = V_UI4(java);
break;
}
case VT_UI4 | VT_BYREF:
{
V_UI4(com) = *V_UI4REF(java);
break;
}
}
break;
}
case VT_UI4 | VT_BYREF:
{
switch (java->vt)
{
case VT_UI4:
{
*V_UI4REF(com) = V_UI4(java);
break;
}
case VT_UI4 | VT_BYREF:
{
*V_UI4REF(com) = *V_UI4REF(java);
break;
}
}
break;
}
case VT_INT:
{
switch (java->vt)
{
case VT_INT:
{
V_INT(com) = V_INT(java);
break;
}
case VT_INT | VT_BYREF:
{
V_INT(com) = *V_INTREF(java);
break;
}
}
break;
}
case VT_INT | VT_BYREF:
{
switch (java->vt)
{
case VT_INT:
{
*V_INTREF(com) = V_INT(java);
break;
}
case VT_INT | VT_BYREF:
{
*V_INTREF(com) = *V_INTREF(java);
break;
}
}
break;
}
case VT_UINT:
{
switch (java->vt)
{
case VT_UINT:
{
V_UINT(com) = V_UINT(java);
break;
}
case VT_UINT | VT_BYREF:
{
V_UINT(com) = *V_UINTREF(java);
break;
}
}
break;
}
case VT_UINT | VT_BYREF:
{
switch (java->vt)
{
case VT_UINT:
{
*V_UINTREF(com) = V_UINT(java);
break;
}
case VT_UINT | VT_BYREF:
{
*V_UINTREF(com) = *V_UINTREF(java);
break;
}
}
break;
}
case VT_CY:
{
switch (java->vt)
{
case VT_CY:
{
V_CY(com) = V_CY(java);
break;
}
case VT_CY | VT_BYREF:
{
V_CY(com) = *V_CYREF(java);
break;
}
}
break;
}
case VT_CY | VT_BYREF:
{
switch (java->vt)
{
case VT_CY:
{
*V_CYREF(com) = V_CY(java);
break;
}
case VT_CY | VT_BYREF:
{
*V_CYREF(com) = *V_CYREF(java);
break;
}
}
break;
}
case VT_DATE:
{
switch (java->vt)
{
case VT_DATE:
{
V_DATE(com) = V_DATE(java);
break;
}
case VT_DATE | VT_BYREF:
{
V_DATE(com) = *V_DATEREF(java);
break;
}
}
break;
}
case VT_DATE | VT_BYREF:
{
switch (java->vt)
{
case VT_DATE:
{
*V_DATEREF(com) = V_DATE(java);
break;
}
case VT_DATE | VT_BYREF:
{
*V_DATEREF(com) = *V_DATEREF(java);
break;
}
}
break;
}
case VT_BSTR:
{
switch (java->vt)
{
case VT_BSTR:
{
V_BSTR(com) = V_BSTR(java);
break;
}
case VT_BSTR | VT_BYREF:
{
V_BSTR(com) = *V_BSTRREF(java);
break;
}
}
break;
}
case VT_BSTR | VT_BYREF:
{
switch (java->vt)
{
case VT_BSTR:
{
*V_BSTRREF(com) = V_BSTR(java);
break;
}
case VT_BSTR | VT_BYREF:
{
*V_BSTRREF(com) = *V_BSTRREF(java);
break;
}
}
break;
}
case VT_DECIMAL:
{
switch (java->vt)
{
case VT_DECIMAL:
{
V_DECIMAL(com) = V_DECIMAL(java);
break;
}
case VT_DECIMAL | VT_BYREF:
{
V_DECIMAL(com) = *V_DECIMALREF(java);
break;
}
}
break;
}
case VT_DECIMAL | VT_BYREF:
{
switch (java->vt)
{
case VT_DECIMAL:
{
*V_DECIMALREF(com) = V_DECIMAL(java);
break;
}
case VT_DECIMAL | VT_BYREF:
{
*V_DECIMALREF(com) = *V_DECIMALREF(java);
break;
}
}
break;
}
}
}