Team LiB
Previous Section Next Section

Using Arrays in COM Interop

Prepare to pull your hair out. Using arrays in COM interop is one of the biggest areas of problems.

From a COM perspective, there are five types of arrays:

Passing an Array of Interface Pointers

The following example demonstrates the managed code for passing an array of pointers.

public class PassingAnArrayByExample
{
       //Create an array object that will hold X number of elements of Y objects.
       public Array T = Array.CreateInstance((typeof(MSXML2.IXMLDOMDocument)),20);

       public TestArrayLib.TestingClass MyObject = new TestArrayLib.TestingClass();
       public PassingAnArrayByExample()
       {
            for (int i=0; i<20 ;i++)
            {
                //Fill the array members with the objects desired,
                //so at this point we have an array of type
                //System.Array.
                 SAFEARRAY(IDispatch))
                //These will be passed to the COM object as a SafeArray(IDispatch).
                //You can then pull out items and call QI to get the interface you
                  want.
                  T.SetValue((new MSXML2.DOMDocumentClass()),i);
                  Console.WriteLine( T.GetValue(i) );
            }
       MyObject.Testme( ref T);
}

In unmanaged code, the IDL definition of the method is as follows:

[id(1), helpstring("method Testme")] HRESULT Testme([in,out]
SAFEARRAY(IDispatch*)* m);

STDMETHODIMP CTesting::Testme(SAFEARRAY** m)
{
IDispatch* pDispatch;long ElementNumber = 0;
HRESULT hr = 0;
//This will pull out by element of the array the object's IDispatch interface.
hr = SafeArrayGetElement(*m, &ElementNumber, &pDispatch);
return S_OK;
}

Passing an Entire C-Style Array to COM Code

The following example demonstrates the managed client code for passing an entire C-style array to COM code.

static void Main(string[] args)
{
           int Size = 5;
           byte[] Buffer = new byte[Size + 1];
           Buffer[0] = 65;
           Buffer[1] = 66;
           Buffer[2] = 67;
           COMTestClass Test = new COMTestClass();
           Test.SetMessage(Buffer, Buffer.Length);
            Console.WriteLine(Buffer[0]);
}

The unmanaged code is as follows:

STDMETHODIMP CCOMTest::SetMessage(BYTE* pBuffer, long size)
{
          *pBuffer = 68;
          return S_OK;
}

The IDL code looks like this:

[id(1), helpstring("method SetMessage")] HRESULT SetMessage
    ([in] BYTE* pBuffer, [in] long size);

You must modify the Microsoft Intermediate Language (MSIL) code generated by Visual Studio or tlbimp.exe because a C-style array parameter looks like a ref parameter, but it is actually treated as a by-value parameter. To modify the IL code, run the following:

ildasm /out:Interop.InteropTesterCOMLib.il Interop.InteropTesterCOMLib.dll

Then modify the method signature twice in the IL code. Open the IL code with Notepad and change the method signature from this:

instance void SetMessage([in] unsigned int8& pBuffer,
[in] int32 size) runtime managed internalcall

to this (you will need to do this twice in the IL code):

instance void SetMessage([in] unsigned int8 [] marshal([+1]) pBuffer,
[in] int32 size) runtime managed internalcall

Save the results.

Then compile the IL code:

ilasm /DLL Interop.InteropTesterCOMLib.IL /RESOURCE=
Interop.InteropTesterCOMLib.res

Finally, add the new DLL as a reference.

The following example demonstrates setting security on a COM interface.

Code Example: Setting Security on a Specific COM Interface
Start example

using System;
using System.Runtime.InteropServices;
using System.Reflection;

namespace Client.Chapter_10___COM_and_.NET_Interoperability
{
        public struct COAUTHIDENTITY
        {
               [MarshalAs(UnmanagedType.LPWStr)]
               public string User;
               public uint UserLength;
               [MarshalAs(UnmanagedType.LPWStr)]
               public string Domain;
               public uint DomainLength;
               [MarshalAs(UnmanagedType.LPWStr)]
               public string Password;
               public uint PasswordLength;
               public uint Flags;
        };
        class Class1
        {
               //Various constants
               const uint EOAC_NONE = 0;
               const uint SEC_WINNT_AUTH_IDENTITY_UNICODE = 2;
               const uint RPC_C_AUTHN_WINNT = 10;
               const uint RPC_C_AUTHZ_NONE = 0;
               const uint RPC_C_AUTHN_LEVEL_DEFAULT = 0;
               const uint RPC_C_IMP_LEVEL_IMPERSONATE = 3;
               [DllImport("Ole32.dll", CharSet = CharSet.Auto)]
               public static extern int CoSetProxyBlanket
                      (IntPtr pProxy, uint dwAuthnSvc, uint dwAuthzSvc,
                       uint
                       pServerPrincName, uint dwAuthLevel,
                       uint dwImpLevel, IntPtr pAuthInfo,
                       uint dwCapabilities);
               [STAThread]

        static void Main(string[] args)
        {
               int hr;
               Guid CLSID =
                   new Guid("1ACD2158-6E0E-48B6-A01C-ACF1CAC48580");
               string machineName = "MyPCName";
               System.Type typeInfo =
                   Type.GetTypeFromCLSID(CLSID,machineName,true);
               Console.WriteLine("Type.GetTypeFromCLSID successful");
               Console.ReadLine();

               object objDCOM = Activator.CreateInstance(typeInfo);
               Console.WriteLine("Activator.CreateInstance successful");
               Console.ReadLine();

               COAUTHIDENTITY Auth = new COAUTHIDENTITY();
               IntPtr pAuth = Marshal.AllocCoTaskMem(28);
              Auth.User = "myusername";
              Auth.UserLength = (uint)Auth.User.Length;
              Auth.Domain = "mydomain";
              Auth.DomainLength = (uint)Auth.Domain.Length;
              Auth.Password = "mypassword";
              Auth.PasswordLength = (uint)Auth.Password.Length;
              Auth.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
              Marshal.StructureToPtr(Auth, pAuth, false);

              hr = CoSetProxyBlanket
                         (Marshal.GetIUnknownForObject(objDCOM), //pProxy
                         RPC_C_AUTHN_WINNT, //dwAuthnSvc
                         RPC_C_AUTHZ_NONE, //dwAuthzSvc
                         0, // pServerPrincName
                         RPC_C_AUTHN_LEVEL_DEFAULT, //dwAuthnLevel
                         RPC_C_IMP_LEVEL_IMPERSONATE, //dwImpLevel
                         pAuth, //pAuthInfo
                         EOAC_NONE); //dwCapabilities
              Console.WriteLine(
                         "CoSetProxyBlanket for IUnknown returned " + hr);
              if (hr != 0)
                        return;

              hr = CoSetProxyBlanket
                         (Marshal.GetIDispatchForObject(objDCOM), //pProxy
                         RPC_C_AUTHN_WINNT, //dwAuthnSvc
                         RPC_C_AUTHZ_NONE, //dwAuthzSvc
                         0, // pServerPrincName
                         RPC_C_AUTHN_LEVEL_DEFAULT, //dwAuthnLevel
                         RPC_C_IMP_LEVEL_IMPERSONATE, //dwImpLevel
                         pAuth, //pAut
                         EOAC_NONE); //dwCapabilities

              Console.WriteLine(
                        "CoSetProxyBlanket for IDispatch returned " + hr);
              if (hr != 0)
                       return;

              object actualReturnValue =
              typeInfo.InvokeMember("TestMe",
              BindingFlags.Default|BindingFlags.InvokeMethod,
              null,objDCOM,null,null,null,null);
              Console.WriteLine("Done");
         }
    }
}
End example

Team LiB
Previous Section Next Section