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?
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
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.
User contributions licensed under CC BY-SA 3.0