#18 VariantVariant

1.12 Accepted
closed-accepted
nobody
COM Data (10)
5
2007-09-06
2007-02-01
Mashman
No

I needed a simple way to handle parmater types of VariantVariant.

Modified Jacob to add new type VariantVariant

Discussion

1 2 > >> (Page 1 of 2)
  • Mashman

    Mashman - 2007-02-01

    VariantVariant code

     
  • clay_shooter

    clay_shooter - 2007-02-04

    Logged In: YES
    user_id=1189284
    Originator: NO

    Is there a test program that shows that this works? I'd hate to put it in and then have it break at some future date without knowing that it broke.

     
  • clay_shooter

    clay_shooter - 2007-02-04

    Logged In: YES
    user_id=1189284
    Originator: NO

    Hello,

    This patch is not yet ready to be accepted. Variant putXXX methods are now private with a Variant putYYY method that sits in front of them and makes sure that the parameter passed in si not null and of the right type. In addition, the toJavaObject in Variant.java eitehr needs to be updated to work for VariantVariant objects or have a comment added that describes why the toJAvaObject() should fail for VariantVariant classes.

     
  • clay_shooter

    clay_shooter - 2007-02-04

    Logged In: YES
    user_id=1189284
    Originator: NO

    There are no getVariantVariant() methods in the patch. Should there be a way to retrieve VariantVariant objects?

     
  • Mashman

    Mashman - 2007-02-05

    Logged In: YES
    user_id=1690557
    Originator: YES

    I've made changes to the code.

    I removed the VariantVariant functionality from the com.jacob.com.VariantVariant class, and put it in com.jacob.com.Variant. Class com.jacob.com.VariantVariant is strictly a wrapper class now.

    In class com.jacob.com.Variant
    Added class variable 'referencedVariant' which will contain the instance of Variant the is being referenced if _this is an instance of VariantVariant

    A Variant of type VariantVariant is only a pointer to another Variant, so manipulation of the Variant - other than setting it's value to reference a different Variant is not required. So a getVariantVariant is not required, the public method getVariant() will return the referencedVariant.

    Added public method 'putVariant(Variant)' to set the class variable referencedVariant, and call native method putVariantVariant(Variant) to initialize the underlying variant.

    Added public method 'getVariant()' this method will return the instance of referencedVariant.

    Changed the signature for native method putVariantVariant(Variant) from protected to private.

    Modified toString() method to handle type VariantVariant
    Modofoed toJavaObject() method to handle type VariantVariant

    Attached is an updated patch zip file.

    The dll I'm using in my project is proprietary, so I'm trying to find something public to make a test
    class out of.

    Matt

    File Added: Jacob Updates.zip

     
  • Mashman

    Mashman - 2007-02-05

    Modified patch zip file

     
  • Mashman

    Mashman - 2007-02-05

    Modified patch zip file

     
  • Mashman

    Mashman - 2007-02-05

    Logged In: YES
    user_id=1690557
    Originator: YES

    I've made changes to the code.

    I removed the VariantVariant functionality from the com.jacob.com.VariantVariant class, and put it in com.jacob.com.Variant. Class com.jacob.com.VariantVariant is strictly a wrapper class now.

    In class com.jacob.com.Variant
    Added class variable 'referencedVariant' which will contain the instance of Variant the is being referenced if _this is an instance of VariantVariant

    A Variant of type VariantVariant is only a pointer to another Variant, so manipulation of the Variant - other than setting it's value to reference a different Variant is not required. So a getVariantVariant is not required, the public method getVariant() will return the referencedVariant.

    Added public method 'putVariant(Variant)' to set the class variable referencedVariant, and call native method putVariantVariant(Variant) to initialize the underlying variant.

    Added public method 'getVariant()' this method will return the instance of referencedVariant.

    Changed the signature for native method putVariantVariant(Variant) from protected to private.

    Modified toString() method to handle type VariantVariant
    Modofoed toJavaObject() method to handle type VariantVariant

    Attached is an updated patch zip file.

    The dll I'm using in my project is proprietary, so I'm trying to find something public to make a test
    class out of.

    Matt

    File Added: Jacob Updates.zip

     
  • clay_shooter

    clay_shooter - 2007-02-07

    Logged In: YES
    user_id=1189284
    Originator: NO

    I've attached an updated version of Variant.

    1) This is built on top of the latest version of Jacob (1.11.1) and your fixes. I've also reworked some of the code.

    1) I've eliminated the VariantVariant() class. The constructor for Variant(Object) now handles VariantVariant types so you can create one with "new Variant(new Variant());"

    2) I'm still trying to understand whether this is an incomplete implementation (which may be fine for now). We can pass a Variant in to a call as a parameter. But we can't get a Variant back out if some windows code creates one. I'm guessing that really can't happen. If not, then I'll remove the getVariantVariant call in Variant.cpp and Variant.h so no one thinks they do anything. the getVariant() method in Variant.java goes to its funky Variant ivar. I'm thinking it should really get it from the underlying windows store like the rest of the calls but don't know for sure.

    File Added: Variant.java

     
  • clay_shooter

    clay_shooter - 2007-02-07

    Reworked Variant.java built from latest branch and 214610

     
  • Mashman

    Mashman - 2007-02-07
     
  • Mashman

    Mashman - 2007-02-07

    Logged In: YES
    user_id=1690557
    Originator: YES

    OK

    1 - That sounds good. I made changes to jacobgen to generate code using VariantVariant, to make things a little easier. I can add VariantVariant to my jacobgen patch.

    2 - I've gone back and forth a couple of times on "what should the behavior of getVariant() be?". One of the main things is - what do I do when the referenced Variant is released? I kind of sidestepped the problem by creating the ivar referencedVariant, but after hearing from you, I think that sticking as closely as posible to the existing model is the best approach.

    Attached is a new version. It follows the existing model very closly. I put in code that will set the variant type to VT_EMPTY when the referenced variant is released.

    Below is a little test code.

    Thanks,
    Matt

    System.setProperty("com.jacob.debug", "true");

    Variant varD = new Variant();
    System.out.println("varD="+varD);

    Variant varVariant = new Variant();
    System.out.println("varVariant="+varVariant);

    varVariant.putVariant(varD);

    System.out.println("varVariant="+varVariant);

    varD.putDouble(10);
    System.out.println("varVariant="+varVariant);

    System.out.println("varVariant.getVariantVariantId = "+varVariant.getVariantVariantId());

    varD.safeRelease();

    System.out.println("varVariant="+varVariant);

    Variant varS = new Variant("A String");
    System.out.println("varS="+varS);

    varVariant.putVariant(varS);

    System.out.println("varVariant.getVariantVariantId = "+varVariant.getVariantVariantId());

    System.out.println("varVariant="+varVariant);
    File Added: Jacob Updates.zip

     
  • clay_shooter

    clay_shooter - 2007-02-08

    Logged In: YES
    user_id=1189284
    Originator: NO

    Hi,

    I'm starting to think this approach isn't going to work reliably. (It may be that I'm thinking hard enough that my mind is getting bent)

    The problem is that we can create multiple references to the same windows memory. We can have more than one Variant pointing at the contained variant data. Its even worse how that I've talked you into supporting the get. We can create multiple Java Variant classes that point to the same memory. The suggested ROT change addresses that only for references created in the current thread. It doesn't find any others. On top of that, we could free the original Java variant that would free the reference. Then all of the getVariant() calls would get pointers to freed memory. That works fine until the memory is overwritten.

    The current scheme uses a scheme JavaVariant(JavaVariant(WindowsVariant))) when we create or get a value. This is because we are using the Variant class for this. Maybe the "right" way to do this is to make your VariantVariant a first class subclass that emulates the behavior of Variant except that it knows the data is supposed to be in a windows variant down below. It only takes regular java objects and primitives and that's all it provides. Then we don't have replicated references to the same memory.

    I think the test and other programs work because we haven't recycled the freed memory. The code is well thought out for your program but I'm not convinced VariantVariant can easily be made to work for the general case.

     
  • clay_shooter

    clay_shooter - 2007-02-08

    Logged In: YES
    user_id=1189284
    Originator: NO

    I'm not even sure that the old way, retaining the ref to the wrapped Variant, works. If the wrapped Variant and the wrapping VariantVariant were created in different threads, they could have their windows memory freed at different times. (Freeing is on a per thread basis when not using autogc). It seems that you have to do this where you hide the fact a Variant is inside the VariantVariant.

    Another option would be to use something other than Variant for the wrapped object. Maybe a VariantInnerVariant class is used that is a subclass of Variant. It would override the safeRelease() and release() methods so that it didn't actually free the underlying memory. It would count on the fact that the Variant (of type VariantVariant) would take of freeing the memory as part of its underlying store. Then the get method would return VariantInnerVariant also so that the returned objects could be freed without corrupting windows memory. Of course then you would have a leak if you createed a VariantInnerVariant instance that you didn't insert into a Variant of type VariantVariant. You'd also have a problem if you had VariantInnerVariants hanging around that pointed at memory freed by the VariantVariant when it was GC'd

    (Are we confused yet? I need a beer.)

     
  • Mashman

    Mashman - 2007-02-09

    Logged In: YES
    user_id=1690557
    Originator: YES

    Hope you got to have your beer - I had mine!

    I really appreciate all you help with this. And, I think I may be on to a working solution.

    I think the main problem I've had is by leaving the the Variant, and VariantVariant somehow coupled.

    I think I might have a solution, it's kind of a combination between the teo previous approaches.

    Thanks,
    Matt

    File Added: Jacob Updates.zip

     
  • Mashman

    Mashman - 2007-02-09
     
  • clay_shooter

    clay_shooter - 2007-04-15

    Logged In: YES
    user_id=1189284
    Originator: NO

    I ran into a need for this. Some of the event callbacks pass a *BSTR. That is actually being passed as a VariantVariant|ByRef. Are most VariantVariants really BSTR structures? If so, then could we just return a String object when doing a toJavaObject on these guys?

     
  • clay_shooter

    clay_shooter - 2007-04-18

    Logged In: YES
    user_id=1189284
    Originator: NO

    How about this

    getVariantVariant() actually returns a java object (Object). Every time we get a VariantVariant,

    1) Variant calls the JNI code to get the enclosed variant
    2) Variant does a toJavaObject on the previously retrieved enclosed variant
    3) Variant zeros out the windows memory reference on the enclosed Variant.
    4) Variant returns the created java object
    5) gc comes along and frees the enclosed variant but it doesn't have a ref to the underlying windows memory any more so the memory stays where it should so the enclosing variantvariant can retrieve the java value again later.

    The put can probably work like you had it before. I've implemented this and it works. Hopefully you can deal with a Java object instead of a Variant when you do the get. It has the bad side effect that you can't modify the enclosed variant. It has the good side effect that it really simplifies the memory management for the standard types. I have no idea if it works for things like enclosed arrays.

    I've made the changes here and will check them into the tree under this request.

     
  • clay_shooter

    clay_shooter - 2007-04-18
    • labels: --> COM Data
    • milestone: --> 1.12 Accepted
    • status: open --> pending-accepted
     
  • clay_shooter

    clay_shooter - 2007-04-18

    Logged In: YES
    user_id=1189284
    Originator: NO

    This code has been checked into CVS

     
  • clay_shooter

    clay_shooter - 2007-04-18

    Logged In: YES
    user_id=1189284
    Originator: NO

    Here is the relavent Variant.java code

    /**
    * Puts a variant into this variant making it type VT_VARIANT.
    * Added 1.12 pre 6
    *
    * @throws IllegalArgumentException
    * if inVariant = null
    * @param objectToBeWrapped A object that is to be referenced by this variant.
    * If objectToBeWrapped is already of type Variant, then it is used.
    * If objectToBeWrapped is not Variant then <code>new Variant(objectToBeWrapped)</code>
    * is called and the result is passed into the com layer
    */
    public void putVariant(Object objectToBeWrapped) {
    if (objectToBeWrapped == null) {
    throw new IllegalArgumentException("Cannot put null in as a variant");
    } else if (objectToBeWrapped instanceof Variant){
    putVariantVariant((Variant)objectToBeWrapped);
    } else {
    Variant inVariant = new Variant(objectToBeWrapped);
    putVariantVariant(inVariant);
    }
    }

    /**
    * All VariantVariant type variants are BYREF.
    *
    * Set the content of this variant to a string (VT_VARIANT|VT_BYREF).
    *
    * Added 1.12 pre 6 - VT_VARIANT support is at an alpha level
    * @param in variant to be wrapped
    *
    */
    private native void putVariantVariant(Variant in);

    /**
    * Used to get the value from a windows type of VT_VARIANT
    * or a jacob Variant type of VariantVariant.
    * Added 1.12 pre 6 - VT_VARIANT support is at an alpha level
    * @return Object a java Object that represents the content of the enclosed Variant
    */
    public Object getVariant() {
    if ((this.getvt() & VariantVariant) == VariantVariant
    && (this.getvt() & VariantByref) == VariantByref) {
    if (JacobObject.isDebugEnabled()){
    JacobObject.debug("About to call getVariantVariant()");
    }
    Variant enclosedVariant = getVariantVariant();
    Object enclosedVariantAsJava = enclosedVariant.toJavaObject();
    // zero out the reference to the underlying windows memory so that
    // it is still only owned in one place by one java object
    // (this object of type VariantVariant)
    //enclosedVariant.putEmpty(); // don't know if this would have had side effects
    if (JacobObject.isDebugEnabled()){
    JacobObject.debug("Zeroing out enclosed Variant's ref to windows memory");
    }
    enclosedVariant.m_pVariant = 0;
    return enclosedVariantAsJava;
    } else {
    throw new IllegalStateException(
    "getVariant() only legal on Variants of type VariantVariant, not "
    + this.getvt());
    }
    }

    /**
    * Returns the variant type via a native method call.
    * Added 1.12 pre 6 - VT_VARIANT support is at an alpha level
    * @return Variant one of the VT_Variant types
    */
    private native Variant getVariantVariant();

     
  • Mashman

    Mashman - 2007-04-18

    Logged In: YES
    user_id=1690557
    Originator: YES

    Hi, a couple of questions

    What does the JNI code look like? Did you use the last version of Variant.cpp and Dispatch.cpp that I attached?

    Below is code from last version of Variant (along with Dispatch) I had sent. I think if you use it, you shouldn't have to dereference the pointer, because only 1 object will exist, the internal pointer (that is the C variant) is a weak reference to the java Variant object - so getVariantVariant doesn't contruct a new java object, it returns the wrapped java object. I think you will find this most closly follows the existing model.

    Let me know what you think, Thanks,
    Matt

    JNIEXPORT void JNICALL Java_com_jacob_com_Variant_putVariantVariant
    (JNIEnv *env, jobject _this, jobject var)
    {

    VARIANT *v = extractVariant(env, _this);
    if (v) {
    env->DeleteGlobalRef((jobject)V_INT_PTR(v));
    VariantClear(v); // whatever was there before

    V_VT(v) = VT_INT_PTR;
    V_INT_PTR(v) = (INT_PTR)env->NewWeakGlobalRef(var);
    }
    }

    JNIEXPORT jobject JNICALL Java_com_jacob_com_Variant_getVariantVariant
    (JNIEnv *env, jobject _this)
    {

    VARIANT *v = extractVariant(env, _this);
    if (v) {

    if (V_VT(v) != (VT_INT_PTR)) {
    return NULL;
    }

    jobject internalVar = (jobject)V_INT_PTR(v);
    try {
    VARIANT *vInternal = extractVariant(env, internalVar);
    if ((unsigned int)vInternal == 0){
    V_VT(v) = VT_EMPTY;
    return _this;
    }

    } catch (...){
    if (v) {
    VariantClear(v);
    V_VT(v) = VT_EMPTY;
    return _this;
    }
    }

    return internalVar;
    }
    return NULL;
    }

     
  • Mashman

    Mashman - 2007-04-18
    • status: pending-accepted --> open-accepted
     
  • clay_shooter

    clay_shooter - 2007-04-19

    Logged In: YES
    user_id=1189284
    Originator: NO

    I don't understand the whole VT_INT_PTR piece in your new code.

    Here is what I have for getVariantVaraint and putVariantVariant based on your earlier code. I like using Java objects instead of Variants in get and put because it works like most of the other gets and puts that return java types rather than windows Variant types. Of course it's not perfect because the enclosed parameter can't be byRef if we pass in a java object. I work around that on the put by letting them pass a Variant in, in which case I just pass that down to the lower layer. The other error right now is that putVariant() should zero out the win-memory pointer after calling putVariantVariant() so that the memory isn't double released. You basically do the same thing but from down in the com layer. I don't mind doing it above in Java but that is because I'm comfortable with it.

    My current code is checked into CVS if you want to check it out.

    I'll have to look at the Dispatch.java code. Its not clear to me whay we need the extra work but I haven't really stared at it.

    /**
    * Puts a variant into a the Variant as its data and sets the type
    * to VT_VARIANT|VT_BYREF.
    * Added 1.12 pre 6
    *
    * */
    JNIEXPORT void JNICALL Java_com_jacob_com_Variant_putVariantVariant
    (JNIEnv *env, jobject _this, jobject var)
    {

    VARIANT *vVar = extractVariant(env, var);
    VARIANT *v = extractVariant(env, _this);

    if (v) {
    VariantClear(v); // whatever was there before

    V_VT(v) = VT_VARIANT|VT_BYREF;
    V_VARIANTREF(v) = vVar;
    }

    }

    /**
    * retrieves the enclosed variant when they are of type VT_VARIANT
    * Added 1.12 pre 6
    *
    * */
    JNIEXPORT jobject JNICALL Java_com_jacob_com_Variant_getVariantVariant
    (JNIEnv *env, jobject _this)
    {

    VARIANT *v = extractVariant(env, _this);
    if (v) {

    if (V_VT(v) != (VT_VARIANT|VT_BYREF)) {
    return NULL;
    }

    //Construct a new Variant
    jclass variantClass = env->FindClass("com/jacob/com/Variant");
    jmethodID defaultCon = env->GetMethodID(variantClass, "<init>", "()V");
    jobject newVariant = env->NewObject(variantClass, defaultCon);

    VARIANT *refVar = V_VARIANTREF(v);
    VARIANT *newV = extractVariant(env, newVariant);

    // we could have made a copy of refV here but we aren't every going to free
    // it outside of the scope of the enclosing context so we will just used the
    // enclosed. This relies on the java layer to zero out its ref to this
    // enclosed variant before the gc can come along and free the memory out from
    // under this enclosing variant.

    jfieldID jf = env->GetFieldID( variantClass, VARIANT_FLD, "I");
    env->SetIntField(newVariant, jf, (unsigned int)refVar);

    return newVariant;
    }

    return NULL;

    }

     
  • clay_shooter

    clay_shooter - 2007-09-06

    Logged In: YES
    user_id=1189284
    Originator: NO

    This code went into 1.12pre6 as test code. No one has complained since then :)

     
1 2 > >> (Page 1 of 2)

Log in to post a comment.

Get latest updates about Open Source Projects, Conferences and News.

Sign up for the SourceForge newsletter:

JavaScript is required for this form.





No, thanks