6

I would like to call LsaOpenPolicy, which takes a LSA_OBJECT_ATTRIBUTES struct. I am using the struct definition from pinvoke.net. This struct has the field public LSA_UNICODE_STRING ObjectName;.

The LSA_OBJECT_ATTRIBUTES MSDN article says:

When you call LsaOpenPolicy, initialize the members of this structure to NULL or zero because the function does not use the information.

And specifically:

ObjectName

    Should be NULL.

While I can assign the other fields of the LSA_OBJECT_ATTRIBUTES struct to IntPtr.Zero (or just plain 0 for the value types), I cannot see a way to do so for ObjectName. Specifically,

Cannot implicitly convert type 'System.IntPtr' to 'LSA_UNICODE_STRING'

What should I do in this case? Should I just initialise a LSA_UNICODE_STRING of length zero (length 0, maximumlength 0, empty/null buffer)? Should I change the LSA_OBJECT_ATTRIBUTES definition so that field is an IntPtr? Should I make it nullable and assign null to that field?

I have very little experience with memory management, so I am rather wary of anything that could cause memory leaks.

Community
  • 1
  • 1
Bob
  • 15,441
  • 3
  • 26
  • 42
  • It might not be the prettiest sollution, but what happens when you cast to an object before you pass the argument? – Jordy Jun 04 '13 at 07:14
  • @Jordy `(LSA_UNICODE_STRING)(object)IntPtr.Zero` compiles, but throws a `InvalidCastException` at runtime. Both methods I suggested at the end of my question work as far as I can tell, but I have no idea if I will run into any problems down the line. – Bob Jun 04 '13 at 07:34
  • Pinvoke is a tricky thing... If it works as you would expect you may count yourself lucky. Basic rule of thumb with Pinvoke: If it works, don't touch it. – Jordy Jun 04 '13 at 07:54

1 Answers1

5

Declare LSA_UNICODE_STRING to be a class rather than a struct. By doing so you make it a reference type. That matches the declaration of LSA_OBJECT_ATTRIBUTES because ObjectName has type PLSA_UNICODE_STRING which is a pointer to the struct. You do need to specify LayoutKind.Sequential when you do this since that's not the default for a class. Once you've made this change, you can set the variable to null.

[StructLayout(LayoutKind.Sequential)]
class PLSA_UNICODE_STRING 
{
    public UInt16 Length;
    public UInt16 MaximumLength;
    public IntPtr Buffer;
}

You can adopt the same policy for LSA_OBJECT_ATTRIBUTES to allow that to be passed as null.

[StructLayout(LayoutKind.Sequential)]
class PLSA_OBJECT_ATTRIBUTES
{
   public uint Length;
   public IntPtr RootDirectory;
   public PLSA_UNICODE_STRING ObjectName;
   public uint Attributes;
   public IntPtr SecurityDescriptor;
   public IntPtr SecurityQualityOfService;
}

[DllImport("advapi32.dll")]
static extern uint LsaOpenPolicy(
    PLSA_UNICODE_STRING SystemName,
    PLSA_OBJECT_ATTRIBUTES ObjectAttributes,
    uint DesiredAccess,
    out IntPtr PolicyHandle
);

Note that the declaration on pinvoke.net erroneously uses SetLastError=true on LsaOpenPolicy. That's wrong because the error code comes back in the return value. I also removed the setting of PreserveSig to true since that is the default. Their declaration of LSA_OBJECT_ATTRIBUTES also appears wrong since the ObjectName parameter has type LSA_UNICODE_STRING which is the struct rather than a pointer to it.

I would advise you to treat what you find on pinvoke.net with extreme scepticism. A large proportion of the declarations on that site are simply incorrect.

You ask about the possibility of using nullable types. According to @JaredPar's answer here, that's not an option.

Community
  • 1
  • 1
David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • OK, I re-worked my answer to match what I think you are asking. Sorry for the misunderstanding first time round. – David Heffernan Jun 04 '13 at 10:49
  • 1
    I actually have a field (`LSA_UNICODE_STRING[] UserRights`) elsewhere (`LsaAddAccountRights`), which does not work with a class (I'm guessing I would have needed to do some marshalling). Declaring both a struct and a class (giving the class the name `PLSA...`, which is fitting anyway), works. Thanks! Also, thank you for digging up the information on nullable types - I forgot they had a different layout in memory. Just out of curiosity, what do you think of declaring `ObjectName` to be of the type `IntPtr`? Would that break anything? – Bob Jun 04 '13 at 11:48
  • That would also be just fine. If you actually wanted to pass a non-null value then you'd have some more work to do. You'd need to marshal struct to pointer, which involves allocation of unmanaged memory. A bit more boilerplate. If you always pass `null` then `IntPtr` is fine. – David Heffernan Jun 04 '13 at 11:51