Thanks a lot Andrew, but there is one thing I dont get here...

  void Next(int cPins, [MarshalAs(UnmanagedType.LPArray)] IntPtr[]
ppPins, out int pcFetched);

and 

    pinArray = new IntPtr[size];
    pinEnum.Next(size, pinArray, out fetched);

Does the Marshaller trust me that the size in pinArray is big enough? I
mean, if I work with my unmanaged code blocks, then he has no choice,
but here - he can hardly know where the stuff is written in the croutine
called.

Thomas Tomiczek
THONA Consulting Ltd.
(Microsoft MVP C#/.NET)

-----Original Message-----
From: Andrew Hopper [mailto:Andy.Hopper@;MINDSPRING.COM] 
Sent: Samstag, 19. Oktober 2002 14:38
To: [EMAIL PROTECTED]
Subject: Re: [ADVANCED-DOTNET] FW: Interop: Handling OUT parameter of
array, gets error "Can not use SizeParamIndex for byref array
parameters."??


Thomas-

If the value of size changed something went terribly wrong, and I
wouldn't expect any code past the Next() call to work.

I'd first try removing the int return value - without the PreserveSig
attribute, the runtime will think the method has a fourth [out, retval]
parameter.  I'd also remove the [In] and [In, Out] attributes from the
parameters.  Parameters are in by default and the ref and out keywords
act like implicit [In, Out] and [Out] attributes, respectively.

I've also learned a slightly "better" way to marshal an array of
pointers that requires less code.  It's a little shorter, and doesn't
require you to allocate unmanaged memory (always a good thing).  I
suggest that you try this approach:

public interface IEnumPins {
  void Next(int cPins, [MarshalAs(UnmanagedType.LPArray)] IntPtr[]
ppPins, out int pcFetched);
  ...
}

public static IPin [] GetPins(IBaseFilter Filter)
{
  int size = 32;
  IntPtr[] pinArray = null;
  int fetched = 0;
  IPin [] pins = null;
  IEnumPins pinEnum = null;

  try
  {
    pinArray = new IntPtr[size];
    Filter.EnumPins( out pinEnum );
    pinEnum.Next(size, pinArray, out fetched);
    pins = new IPin[fetched];
    for (int i = 0; i < fetched; i++)
    {
      pins [i] = (IPin)Marshal.GetObjectForIUnknown(pinArray[i]);
    }
  }
  finally
  {
    if (pinEnum != null)
    {
      Marshal.ReleaseComObject (pinEnum);
    }
  }
  return pins;
}

I understand your frustration with this part of COM interop, but it's
really not so bad.  Passing arrays (and structs that contain pointers to
arrays) just happens to be the hardest part to master.

-Andy

-----Original Message-----
From: Moderated discussion of advanced .NET topics.
[mailto:ADVANCED-DOTNET@;DISCUSS.DEVELOP.COM] On Behalf Of Thomas
Tomiczek
Sent: Friday, October 18, 2002 4:47 AM
To: [EMAIL PROTECTED]
Subject: Re: [ADVANCED-DOTNET] FW: Interop: Handling OUT parameter of
array, gets error "Can not use SizeParamIndex for byref array
parameters."??


Andrew - I hve to ge this back up. Found the time to try it out today,
but it breaks.

This is the interface I have now (part of, only):

        public interface IEnumPins {

                int Next(
                        [In] int cPins,
                        [In, Out] ref IntPtr ppPins, out int pcFetched);

And this is my code- basically a copy of your code trying to get the
first 32 pins.

                public static IPin [] GetPins (IBaseFilter Filter) {
                        int size = 32;
                        IntPtr pPinArrayUM = IntPtr.Zero;
                        int fetched = 0;
                        IPin [] pins = new IPin [0];

                        IEnumPins pinEnum = null;
                        try {
                                int ptrsize =
Marshal.SizeOf(typeof(IntPtr));
                                pPinArrayUM = Marshal.AllocCoTaskMem
(size * ptrsize);

                                Filter.EnumPins( out pinEnum );

                                pinEnum.Next (size, ref pPinArrayUM, out
fetched);
                                pins = new IPin [fetched];
                                for (int i = 0; i < fetched; i++) {
                                        IntPtr pPin = Marshal.ReadIntPtr
(pPinArrayUM, i*ptrsize);
                                        pins [i] = (IPin)
Marshal.GetObjectForIUnknown (pPin);
                                }
                        } finally {
                                if (pinEnum != null) {
                                        Marshal.ReleaseComObject
(pinEnum);
                                }
                                if (pPinArrayUM != IntPtr.Zero) {
                                        Marshal.FreeCoTaskMem
(pPinArrayUM);
                                }
                        }
                        return pins;
                }

I get the following erroneus behavior when single stepping:
(a) after calling the pinEnum.Next function, the debugger changes the
values shown for pPinArrayUM AND size - they change both their values to
something pretty high. Looks like the pointers are being overwritten.
(b) when continuing, the followng error happens at the first
GetObjectForIUnknown:
Unhandled Exception: System.NullReferenceException: Object reference not
set to an instance of an object.
   at System.Runtime.InteropServices.Marshal.GetObjectForIUnknown(IntPtr
pUnk)
   at
ThonaConsulting.DirectShow.Utilities.FilterUtility.GetPins(IBaseFilter
Filter) in
c:\work\thonaconsulting\directshow\thonaconsulting.directshow\utilities\
filterutility.cs:line 31

This looks very much like the data is being written to the wrong areas
upon return. I am pretty sure it is a totally stupid error, but I dont
find it.

I just wish, interop would be more powerfull.

Thomas

You can read messages from the Advanced DOTNET archive, unsubscribe from
Advanced DOTNET, or subscribe to other DevelopMentor lists at
http://discuss.develop.com.

You can read messages from the Advanced DOTNET archive, unsubscribe from Advanced 
DOTNET, or
subscribe to other DevelopMentor lists at http://discuss.develop.com.

Reply via email to