SAFEARRAY* & COM Interop

Discussion in 'Microsoft VC .NET' started by Guest, Sep 18, 2006.

  1. Guest

    Guest Guest

    I’ve got a C# library that I’ve built into a COM component that will be used
    from a VC++ 6 application and while the creation of the COM object side of
    things seem to be working fine, using the object fully is another matter.

    On the C# side of things I’ve got an object defined like so:

    [ClassInterface(ClassInterfaceType.None)]
    public class PID : IPID
    {
    private string type;
    private int number;
    private PID[] pids;

    public string Type
    {
    get { return type;}
    set { type = value;}
    }

    public int Number
    {
    get { return number;}
    set { number = value;}
    }

    public PID[] PIDs
    {
    get { return pids;}
    set { pids = value;}
    }

    public PID(){}

    public PID(string type, int number)
    {
    this.type = type;
    this.number = number;
    this.pids = new PID[0];
    }

    public PID(string type, int number, PID[] pids)
    {
    this.type = type;
    this.number = number;
    this.pids = pids;
    }
    }

    I can instantiate an instance of this object in the MFC app with no
    problem... the problem is when I try to set the PIDs array to... anything but
    null really.

    The issue is that the C# array is viewed by the MFC app as pointer to a
    SAFEARRAY and all attempts to create an array of PID objects and assign them
    to the PIDs property fails.

    I’ve tried straight up (and cheesy) typecasting:

    IPIDPtr pPID1(__uuidof(PID));
    IPID *pid1 = pPID1;
    ....
    IPID *pidArray1[2];
    pidArray1[0] = pid2;
    pidArray1[1] = pid3;
    pid1->PIDs = (SAFEARRAY*)pidArray1;

    Which causes a friendly “User breakpoint called from code at 0x7c901230†and
    points to code I do not have the debug symbols for.

    I also tried explicitly creating a SAFEARRAY with SafeArrayCreate:

    SAFEARRAY * psa;
    SAFEARRAYBOUND rgsabound[1];

    rgsabound[0].lLbound = 0;
    rgsabound[0].cElements = 2;

    psa = SafeArrayCreate(VT_VARIANT, 1, rgsabound);

    LONG index = 0;
    SafeArrayPutElement(psa, &index, &pid2);
    index = 1;
    SafeArrayPutElement(psa, &index, &pid3);

    fi->PIDs = psa;

    In this example everything works fine up until we assign PIDs (but not when
    we set PIDs to null)... then we get an exception which reads:

    Unhandled exception at 0x7c81eb33 in MFCWSTestApp.exe: Microsoft C++
    exception: _com_error @ 0x0012f8c4.

    When the following line is called from the .tli wrapper:

    inline void IFileInformation::putPIDs ( SAFEARRAY * _arg1 ) {
    _com_dispatch_method(this, 0x60020004, DISPATCH_PROPERTYPUT,
    VT_EMPTY, NULL,
    L"\x2009", _arg1);
    }



    Thinking that I should be a little more explicit with regards to the size of
    the elements (and based on another example) I tried the following:

    SAFEARRAY *params = SafeArrayCreateVector(VT_R4, 0, 2);

    int paramMin = 0;

    SafeArrayAccessData(params, (void**)&pid1);
    memcpy(&paramMin, &pid1, sizeof(IPID));
    SafeArrayUnaccessData(params);

    paramMin = 1;
    SafeArrayAccessData(params, (void**)&pid2);
    memcpy(&paramMin, &pid2, sizeof(PID));
    SafeArrayUnaccessData(params);

    This test behaves similarly to the previous one in that the bulk of the code
    executes without any error... but then an unhandled exception is thrown the
    moment we try to assign PIDs to anything... including null:

    Unhandled exception at 0x7c81eb33 in MFCWSTestApp.exe: Microsoft C++
    exception: _com_error @ 0x0012f894.

    Because of the far easier to use environment and debugger I have spent a
    fair amount of time trying to resolve this issue within the VC7.1
    environment, unfortunately both VC6 and VC7 have the exact same issues with
    the code in question.

    Aside from the obvious issue of trying to use VC6... does anyone see what I
    might be doing wrong here or have any ideas on how I could fix it?
     
    Guest, Sep 18, 2006
    #1
    1. Advertisements

  2. Guest

    SvenC Guest

    Hi Brendan,

    see inline

    "Brendan Grant" <> wrote in message
    news:...
    > I've got a C# library that I've built into a COM component that will be
    > used
    > from a VC++ 6 application and while the creation of the COM object side of
    > things seem to be working fine, using the object fully is another matter.
    >
    > On the C# side of things I've got an object defined like so:
    >
    > [ClassInterface(ClassInterfaceType.None)]
    > public class PID : IPID
    > {

    <snip/>
    > private PID[] pids;

    <snip/>
    > public PID[] PIDs
    > {
    > get { return pids;}
    > set { pids = value;}
    > }


    What does the COM declaration in C++ code look like? Have a look at your tlh
    file. Is the retval a VARIANT* or a SAFEARRAY* ?
    What is the definition of the interface IPID?
    What do you expect to return there anyways? You have a class PID which
    exposes an array of PID instances, each exposing a PID array again?
    I am astonished that you can return a PID[]? To my knowledge you cannot
    expose a class as parameter type, just an interface. Is it really PID[] or
    IPID[] what you are returning?

    <snip/>

    >
    > I can instantiate an instance of this object in the MFC app with no
    > problem... the problem is when I try to set the PIDs array to... anything
    > but
    > null really.
    >
    > The issue is that the C# array is viewed by the MFC app as pointer to a
    > SAFEARRAY and all attempts to create an array of PID objects and assign
    > them
    > to the PIDs property fails.
    >
    > I've tried straight up (and cheesy) typecasting:
    >
    > IPIDPtr pPID1(__uuidof(PID));
    > IPID *pid1 = pPID1;


    When you take a raw interface pointer you should call AddRef and Release.
    You get a little help here because pPID1 is a smartpointer so for its
    lifetime pid1 is valid also. So why not you pPID1 directly and remove pid1.

    > ...
    > IPID *pidArray1[2];
    > pidArray1[0] = pid2;
    > pidArray1[1] = pid3;
    > pid1->PIDs = (SAFEARRAY*)pidArray1;


    What is pid1 and pid2? Have they been initialized?

    > Which causes a friendly "User breakpoint called from code at 0x7c901230"
    > and
    > points to code I do not have the debug symbols for.
    >
    > I also tried explicitly creating a SAFEARRAY with SafeArrayCreate:
    >
    > SAFEARRAY * psa;
    > SAFEARRAYBOUND rgsabound[1];
    >
    > rgsabound[0].lLbound = 0;
    > rgsabound[0].cElements = 2;
    >
    > psa = SafeArrayCreate(VT_VARIANT, 1, rgsabound);
    >
    > LONG index = 0;
    > SafeArrayPutElement(psa, &index, &pid2);
    > index = 1;
    > SafeArrayPutElement(psa, &index, &pid3);


    This looks the most promising. But you made some small mistakes:
    I guess pid2 and pid3 are of type IPID*, correct? If so you must not use &,
    just pass pid2 and pid3 as they are already pointers so they are valid for
    void*.
    BUT: void* ist used because you have absolutely no type safety. The compiler
    trusts you, and infect you lied to him ;)
    You said: I want a safearray of VARIANTs. And later on you when you should
    pass a VARIANT through void* you put an IPID* in there.
    I guess you should setup two VARIANTs where you set the type to VT_UNKNOWN
    and set punkVal to pid2 and pid3.

    VARIANT v1, v2;
    v1.vt = v2.vt = VT_UNKNOWN;
    v1.punkVal = pid2; // I assume pid2 is AddReffed and you no longer use pid2
    later on, so you transfer ownership to v1.
    v2.punkVal = pid3; // same assumption

    Now you call:
    SafeArrayPutElements(psa, &index, &v1); // same for v2

    > fi->PIDs = psa;
    >
    > In this example everything works fine up until we assign PIDs (but not
    > when
    > we set PIDs to null)... then we get an exception which reads:
    >
    > Unhandled exception at 0x7c81eb33 in MFCWSTestApp.exe: Microsoft C++
    > exception: _com_error @ 0x0012f8c4.
    >
    > When the following line is called from the .tli wrapper:
    >
    > inline void IFileInformation::putPIDs ( SAFEARRAY * _arg1 ) {
    > _com_dispatch_method(this, 0x60020004, DISPATCH_PROPERTYPUT,
    > VT_EMPTY, NULL,
    > L"\x2009", _arg1);
    > }


    How comes IFileInformation into play? I thought we were using IPID?

    >
    >
    > Thinking that I should be a little more explicit with regards to the size
    > of
    > the elements (and based on another example) I tried the following:
    >
    > SAFEARRAY *params = SafeArrayCreateVector(VT_R4, 0, 2);


    Why do you create an array of doubles when you want to store interface
    pointers?

    >
    > int paramMin = 0;
    >
    > SafeArrayAccessData(params, (void**)&pid1);
    > memcpy(&paramMin, &pid1, sizeof(IPID));
    > SafeArrayUnaccessData(params);
    >
    > paramMin = 1;
    > SafeArrayAccessData(params, (void**)&pid2);
    > memcpy(&paramMin, &pid2, sizeof(PID));
    > SafeArrayUnaccessData(params);


    Why do you copy values 0 and 1 at the adresses of pid1 and pid2. What type
    are pid1 and pid2?

    > This test behaves similarly to the previous one in that the bulk of the
    > code
    > executes without any error... but then an unhandled exception is thrown
    > the
    > moment we try to assign PIDs to anything... including null:
    >
    > Unhandled exception at 0x7c81eb33 in MFCWSTestApp.exe: Microsoft C++
    > exception: _com_error @ 0x0012f894.
    >
    > Because of the far easier to use environment and debugger I have spent a
    > fair amount of time trying to resolve this issue within the VC7.1
    > environment, unfortunately both VC6 and VC7 have the exact same issues
    > with
    > the code in question.
    >
    > Aside from the obvious issue of trying to use VC6... does anyone see what
    > I
    > might be doing wrong here or have any ideas on how I could fix it?


    --
    SvenC
     
    SvenC, Sep 18, 2006
    #2
    1. Advertisements

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. JC
    Replies:
    1
    Views:
    733
    Nicholas Paldino [.NET/C# MVP]
    Jul 8, 2003
  2. Vadym Stetsyak

    some help with interop

    Vadym Stetsyak, Aug 2, 2003, in forum: Microsoft VC .NET
    Replies:
    1
    Views:
    538
    Mattias Sjögren
    Aug 2, 2003
  3. Ahmad Jalil Qarshi

    Problem with SAFEARRAY as property accessors.

    Ahmad Jalil Qarshi, Mar 24, 2005, in forum: Microsoft VC .NET
    Replies:
    5
    Views:
    1,042
    Igor Tandetnik
    Mar 25, 2005
  4. Maxim

    Convert SafeArray to managed array

    Maxim, Apr 10, 2007, in forum: Microsoft VC .NET
    Replies:
    5
    Views:
    682
    Brian Muth
    Apr 12, 2007
  5. Christian Schmidt

    Marshal SafeArray as array<T>

    Christian Schmidt, Jul 9, 2007, in forum: Microsoft VC .NET
    Replies:
    1
    Views:
    372
    Carl Daniel [VC++ MVP]
    Jul 10, 2007
Loading...

Share This Page