views:

519

answers:

2

I'm attempting to write a couple of NAnt tasks for interacting with Microsoft Virtual Server 2005 R2 SP1, and I've lifted the code found on "Virtual PC Guy's WebLog", in the "Controlling Virtual Server through PowerShell" post.

It doesn't work: I always get a failure when calling CreateVirtualMachine:

System.Runtime.InteropServices.COMException (0x80070542): Either a required impersonation level was not provided, or the provided impersonation level is invalid. (Exception from HRESULT: 0x80070542)

at Microsoft.VirtualServer.Interop.VMVirtualServerClass.CreateVirtualMachine(String configurationName, String configurationPath)

My code is as follows:

var virtualServer = new VMVirtualServerClass();
SetSecurity(virtualServer);
var virtualMachine = virtualServer.CreateVirtualMachine("TEST",
    @"D:\Virtual Server\TEST.vmc");

...where SetSecurity is defined as follows:

    private static void SetSecurity(object dcomObject)
    {
        IntPtr pProxy = Marshal.GetIUnknownForObject(dcomObject);
        int hr = CoSetProxyBlanket(pProxy,
            RPC_C_AUTHN_DEFAULT,
            RPC_C_AUTHZ_DEFAULT,
            IntPtr.Zero,
            RPC_C_AUTHN_LEVEL_PKT_PRIVACY,
            RPC_C_IMP_LEVEL_IMPERSONATE,
            IntPtr.Zero,
            EOAC_DYNAMIC_CLOAKING);
        Marshal.ThrowExceptionForHR(hr);
    }

    private const uint RPC_C_AUTHN_NONE = 0;
    private const uint RPC_C_AUTHN_WINNT = 10;
    private const uint RPC_C_AUTHN_DEFAULT = 0xFFFFFFFF;

    private const uint RPC_C_AUTHZ_NONE = 0;
    private const uint RPC_C_AUTHZ_DEFAULT = 0xFFFFFFFF;

    private const uint RPC_C_AUTHN_LEVEL_DEFAULT = 0;
    private const uint RPC_C_AUTHN_LEVEL_PKT_PRIVACY = 6;

    private const uint RPC_C_IMP_LEVEL_IDENTIFY = 2;
    private const uint RPC_C_IMP_LEVEL_IMPERSONATE = 3;

    private const uint EOAC_NONE = 0;
    private const uint EOAC_DYNAMIC_CLOAKING = 0x40;
    private const uint EOAC_DEFAULT = 0x0800;

    [DllImport("Ole32.dll")]
    public static extern int CoSetProxyBlanket(IntPtr pProxy,
            UInt32 dwAuthnSvc,
            UInt32 dwAuthzSvc,
            IntPtr pServerPrincName,
            UInt32 dwAuthnLevel,
            UInt32 dwImpLevel,
            IntPtr pAuthInfo,
            UInt32 dwCapabilities);

If I write a standalone program and add a call to CoInitializeSecurity, then it works. However, I don't want a standalone program -- I want a set of NAnt tasks (so a DLL), and I don't want to call CoInitializeSecurity, because there's no way of guaranteeing that some other NAnt task won't have called it already.

Has anyone got this working?

+1  A: 

It's possible you're running into a fundamental problem with respect to using CoSetProxyBlanket from managed code. Unfortunately, there is no reiable way to interop with this method in managed code due to the way the CLR marshals interfaces.

Here are a couple of blogs entries that describe this problem

JaredPar
Wrapping the Virtual Server interfaces in a simple ATL wrapper seems to be the best solution at the moment. It's not quote "10 minutes work" as one of those posts says, though, purely because there are so many interfaces to wrap if you want the whole lot. I guess it might be possible to generate code from the type library...
Roger Lipscombe
+1  A: 

For what it's worth, it looks like .NET 4.0 will add a new method GetObjectForIUnknownWithBlanket (under System.Runtime.InteropServices.Marshal) to help address this issue. From the (Beta) MSDN article:

GetObjectForIUnknownWithBlanket wraps IUnknown in a unique managed object and calls the CoSetProxyBlanket function on all requested interfaces. It ensures that a unique object is returned instead of looking in the cache to match the given IUnknown to an existing object.

I haven't tried it yet, but it looks promising.

And by the way, great question and great accepted answer! I just encountered the same problem, and this was the only place I found a proper explanation.

Reuben
GetObjectForIUnknownWithBlanket call looks like it might be exactly what's required.
Roger Lipscombe