Как правильно замаршалить ptr-to-ptr, принимающий NULL
От: Mr. None Россия http://mrnone.blogspot.com
Дата: 17.02.11 15:36
Оценка: 24 (2)
Есть такая WIN API функция:

DWORD GetNamedSecurityInfo(
  LPTSTR pObjectName,
  SE_OBJECT_TYPE ObjectType,
  SECURITY_INFORMATION SecurityInfo,
  PSID* ppsidOwner,
  PSID* ppsidGroup,
  PACL* ppDacl,
  PACL* ppSacl,
  PSECURITY_DESCRIPTOR* ppSecurityDescriptor
);


В числе прочих она принимает 4 опциаональных указателя на указатель: ppsidOwner, ppsidGroup, ppDacl и ppSacl. Какждый из этих параметров может принимать NULL — это охначает, что данные тебе не нужны.

Возникла необходимость вызвать эту функцию из C# и встал вопрос, как же её корректно объявить.


Решение в лоб не работает:
[DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
internal static extern uint
GetNamedSecurityInfo(
    string pObjectName,
    SE_OBJECT_TYPE objectType,
    SECURITY_INFORMATION securityInfo,
    ref IntPtr pSidOwner,
    ref IntPtr pSidGroup,
    ref IntPtr pDacl,
    ref IntPtr pSacl,
    ref IntPtr pSecurityDescriptor);

ибо в этом случае передать null или IntPtr.Zero в опциональные параметры не получится.


Поэтому немного подумав нашёл 2-а альтернативных решения, первое сложно в сипользовании, но имеет адекватную декларацию самой функции. Второе просто использовать, но декларация функции мягко говоря не очевидна. Душой склоняюсь ко второму, но боюсь, что есть какой-то подвох, поэтому прошу совета, кто что может сказать.

Итак, решение первое — ручное получение указателя на указатель с помощью AllocHGlobal / StructureToPtr
// декларация
[DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
internal static extern uint
GetNamedSecurityInfo(
    string pObjectName,
    SE_OBJECT_TYPE objectType,
    SECURITY_INFORMATION securityInfo,
    IntPtr pSidOwner,
    IntPtr pSidGroup,
    IntPtr pDacl,
    IntPtr pSacl,
    ref IntPtr pSecurityDescriptor);

//...
// использование:

// сделали pointer-to-pointer вручную
IntPtr ppSid = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(IntPtr)));
Marshal.StructureToPtr(IntPtr.Zero, ppSid, false);

// вызвали (NULL в данном случае - это IntPtr.Zero)
uint result = NativeMethods.GetNamedSecurityInfo(name,
    NativeMethods.SE_OBJECT_TYPE.SE_FILE_OBJECT,
    NativeMethods.SECURITY_INFORMATION.Owner,
    ppSid, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, ref sd);

// "разыменовали" результат
IntPtr pSid = (System.IntPtr)Marshal.PtrToStructure(ppSid, typeof(IntPtr));

// не забыли почистить за собой
Marshal.FreeHGlobal(ppSid);



Решение второе: используем тот факт, что указатель-на-указатель — это есть массив указателей из одного элемента:
// декларация
[DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
internal static extern uint
GetNamedSecurityInfo(
    string pObjectName,
    SE_OBJECT_TYPE objectType,
    SECURITY_INFORMATION securityInfo,

    [MarshalAs(UnmanagedType.LPArray, SizeConst = 1)]
    IntPtr[] pSidOwner,

    [MarshalAs(UnmanagedType.LPArray, SizeConst = 1)]
    IntPtr[] pSidGroup,

    [MarshalAs(UnmanagedType.LPArray, SizeConst = 1)]
    IntPtr[] pDacl,

    [MarshalAs(UnmanagedType.LPArray, SizeConst = 1)]
    IntPtr[] pSacl,

    ref IntPtr pSecurityDescriptor);

// ...
// использование
// сделали "pointer-to-pointer"
IntPtr[] ppSid = new IntPtr[1];
ppSid[0] = IntPtr.Zero;

// вызвали (NULL в данном случае - это null :) )
uint result = NativeMethods.GetNamedSecurityInfo(name,
    NativeMethods.SE_OBJECT_TYPE.SE_FILE_OBJECT,
    NativeMethods.SECURITY_INFORMATION.Owner,
    ppSid, null, null, null, ref sd);

// "разыменовали"
IntPtr pSid = ppSid[0];

// всё



Преимущества второго варианта очевидны, все операции по выделению памяти, копированию (если они есть) остаются на совести среди. Вот только чиста ли эта совесть? Нет ли здесь каких-то сркытых утечек или проблем, уже больно подозрительным решение кажется . Что скажет многоуважаемый ALL?
Компьютер сделает всё, что вы ему скажете, но это может сильно отличаться от того, что вы имели в виду.
Re: Как правильно замаршалить ptr-to-ptr, принимающий NULL
От: _FRED_ Черногория
Дата: 17.02.11 16:43
Оценка: +1
Здравствуйте, Mr. None, Вы писали:

MN>Есть такая WIN API функция:

MN>DWORD GetNamedSecurityInfo(
MN>  LPTSTR pObjectName,
MN>  SE_OBJECT_TYPE ObjectType,
MN>  SECURITY_INFORMATION SecurityInfo,
MN>  PSID* ppsidOwner,
MN>  PSID* ppsidGroup,
MN>  PACL* ppDacl,
MN>  PACL* ppSacl,
MN>  PSECURITY_DESCRIPTOR* ppSecurityDescriptor
MN>);

MN>В числе прочих она принимает 4 опциаональных указателя на указатель: ppsidOwner, ppsidGroup, ppDacl и ppSacl. Какждый из этих параметров может принимать NULL — это охначает, что данные тебе не нужны.
MN>Возникла необходимость вызвать эту функцию из C# и встал вопрос, как же её корректно объявить.

Можно нагенерить перегрузок объявления метода на все случаи жизни.
Help will always be given at Hogwarts to those who ask for it.
Re: Как правильно замаршалить ptr-to-ptr, принимающий NULL
От: sergey_shandar США http://getboost.codeplex.com/
Дата: 17.02.11 17:04
Оценка:
Здравствуйте, Mr. None, Вы писали:

Отличное решение, проблем вроде не вижу.

Насколько я понимаю, нужно показать что
DWORD WINAPI GetNamedSecurityInfo(
  __in       LPTSTR pObjectName,
  __in       SE_OBJECT_TYPE ObjectType,
  __in       SECURITY_INFORMATION SecurityInfo,
  __out_opt  PSID *ppsidOwner,
  __out_opt  PSID *ppsidGroup,
  __out_opt  PACL *ppDacl,
  __out_opt  PACL *ppSacl,
  __out_opt  PSECURITY_DESCRIPTOR *ppSecurityDescriptor
);

тоже самое по поведению что и
DWORD WINAPI GetNamedSecurityInfo(
  __in       LPTSTR pObjectName,
  __in       SE_OBJECT_TYPE ObjectType,
  __in       SECURITY_INFORMATION SecurityInfo,
  __in       PSID (*ppsidOwner)[1],
  __in       PSID (*ppsidGroup)[1],
  __in       PACL (*ppDacl)[1],
  __in       PACL (*ppSacl)[1],
  __in       PSECURITY_DESCRIPTOR (*ppSecurityDescriptor)[1]
);


Остальное, вроде как, система должна сделать сама.
getboost.codeplex.com
citylizard.codeplex.com
Re: Как правильно замаршалить ptr-to-ptr, принимающий NULL
От: Pavel Dvorkin Россия  
Дата: 17.02.11 17:18
Оценка: 6 (1)
Здравствуйте, Mr. None, Вы писали:


MN>Итак, решение первое — ручное получение указателя на указатель с помощью AllocHGlobal / StructureToPtr

MN>[c#]
MN>// декларация
MN>[DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
MN>internal static extern uint
MN>GetNamedSecurityInfo(
MN> string pObjectName,
MN> SE_OBJECT_TYPE objectType,
MN> SECURITY_INFORMATION securityInfo,
MN> IntPtr pSidOwner,
MN> IntPtr pSidGroup,
MN> IntPtr pDacl,
MN> IntPtr pSacl,
MN> ref IntPtr pSecurityDescriptor);

MN>//...

MN>// использование:

MN>// сделали pointer-to-pointer вручную

MN>IntPtr ppSid = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(IntPtr)));
MN>Marshal.StructureToPtr(IntPtr.Zero, ppSid, false);

Что-то мне это решение не нравится, и вот почему. Правда, не исключено, что я ошибаюсь, но не в плане этой функции, а в интерфейсе с дотнетом.

Эта функция сама выделяет память и возвращает указатель на нее через pSecurityDescriptor. Соответственно потом надо LocalFree, как сказано в документации.

ppSecurityDescriptor
A pointer to a variable that receives a pointer to the security descriptor of the object. When you have finished using the pointer, free the returned buffer by calling the LocalFree function.

А вот под pSidOwner, pSidGroup,pDacl и pAcl она память не выделяет. Указатели возвращаются, а память не выделяется. Дело в том. что эти 4 указателя показывают куда-то внутрь блока памяти, выделенного под pSecurityDescriptor. Поэтому их и освобождать не надо.

If the ppsidOwner, ppsidGroup, ppDacl, and ppSacl parameters are non-NULL, and the SecurityInfo parameter specifies that they be retrieved from the object, those parameters will point to the corresponding parameters in the security descriptor returned in ppSecurityDescriptor.

Поэтому зачем тут AllocHGlobal — я не понял.

MN>// вызвали (NULL в данном случае — это IntPtr.Zero)

MN>uint result = NativeMethods.GetNamedSecurityInfo(name,
MN> NativeMethods.SE_OBJECT_TYPE.SE_FILE_OBJECT,
MN> NativeMethods.SECURITY_INFORMATION.Owner,
MN> ppSid, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, ref sd);

MN>// "разыменовали" результат

MN>IntPtr pSid = (System.IntPtr)Marshal.PtrToStructure(ppSid, typeof(IntPtr));

MN>// не забыли почистить за собой

MN>Marshal.FreeHGlobal(ppSid);

Это ты свой блок удалил (как я выше отметил, непонятно зачем нужный). А вот память, выделенную ей — не удалил. Имеем memory leak в неуправляемой памяти.

Со вторым решением разбирайся сам — в этом плане.

А вообще-то есть

http://pinvoke.net/default.aspx/advapi32/GetNamedSecurityInfo.html
With best regards
Pavel Dvorkin
Re[2]: Как правильно замаршалить ptr-to-ptr, принимающий NUL
От: sergey_shandar США http://getboost.codeplex.com/
Дата: 17.02.11 17:58
Оценка:
Здравствуйте, Pavel Dvorkin, Вы писали:

PD>Здравствуйте, Mr. None, Вы писали:


PD>Эта функция сама выделяет память и возвращает указатель на нее через pSecurityDescriptor. Соответственно потом надо LocalFree, как сказано в документации.


PD>ppSecurityDescriptor

PD>A pointer to a variable that receives a pointer to the security descriptor of the object. When you have finished using the pointer, free the returned buffer by calling the LocalFree function.

PD>А вот под pSidOwner, pSidGroup,pDacl и pAcl она память не выделяет. Указатели возвращаются, а память не выделяется. Дело в том. что эти 4 указателя показывают куда-то внутрь блока памяти, выделенного под pSecurityDescriptor. Поэтому их и освобождать не надо.


PD>If the ppsidOwner, ppsidGroup, ppDacl, and ppSacl parameters are non-NULL, and the SecurityInfo parameter specifies that they be retrieved from the object, those parameters will point to the corresponding parameters in the security descriptor returned in ppSecurityDescriptor.


Похоже что так:
[DllImport("kernel32.dll", SetLastError=true)]
static extern IntPtr LocalFree(IntPtr hMem);

[DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
internal static extern uint
GetNamedSecurityInfo(
    string pObjectName,
    SE_OBJECT_TYPE objectType,
    SECURITY_INFORMATION securityInfo,

    [MarshalAs(UnmanagedType.LPArray, SizeConst = 1)]
    IntPtr[] pSidOwner,

    [MarshalAs(UnmanagedType.LPArray, SizeConst = 1)]
    IntPtr[] pSidGroup,

    [MarshalAs(UnmanagedType.LPArray, SizeConst = 1)]
    IntPtr[] pDacl,

    [MarshalAs(UnmanagedType.LPArray, SizeConst = 1)]
    IntPtr[] pSacl,

    [MarshalAs(UnmanagedType.LPArray, SizeConst = 1)]
    IntPtr[] pSecurityDescriptor);


Причем, после использования pSecurityDescriptor[0] нужно вызвать LocalFree(pSecurityDescriptor[0]). Остальные указатели считать смещениями: SidOwnerOffset = pSidOwner[0] — pSecurityDescriptor[0], ...
getboost.codeplex.com
citylizard.codeplex.com
Re[2]: Как правильно замаршалить ptr-to-ptr, принимающий NUL
От: Mr. None Россия http://mrnone.blogspot.com
Дата: 17.02.11 18:07
Оценка:
Здравствуйте, Pavel Dvorkin, Вы писали:

PD>Здравствуйте, Mr. None, Вы писали:



MN>>Итак, решение первое — ручное получение указателя на указатель с помощью AllocHGlobal / StructureToPtr

...
MN>>//...
MN>>// использование:

MN>>// сделали pointer-to-pointer вручную

MN>>IntPtr ppSid = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(IntPtr)));
MN>>Marshal.StructureToPtr(IntPtr.Zero, ppSid, false);

PD>Что-то мне это решение не нравится, и вот почему. Правда, не исключено, что я ошибаюсь, но не в плане этой функции, а в интерфейсе с дотнетом.


PD>Эта функция сама выделяет память и возвращает указатель на нее через pSecurityDescriptor. Соответственно потом надо LocalFree, как сказано в документации.


Это естественно, просто этот аспект использования к сути вопроса отношения не имеет, поэтому я оставил его за кадром. Тут как раз всё понятно.

PD>А вот под pSidOwner, pSidGroup,pDacl и pAcl она память не выделяет. Указатели возвращаются, а память не выделяется.

...

PD>Поэтому зачем тут AllocHGlobal — я не понял.


Объясняю. Нам нужно передать указатель на указатель на на некую переменную (в данном случае на переменную типа SID), то есть в случае C++ сделать следующее:
PSDI pSid = 0;
PSID *ppSdi = &pSid;
func(ppSid);
// используем pSid


Проблема в том, что в C# у нас нет ни указателей, ни операции взятия адреса. У нас есть всего лишь тип "адрес блока неуправляемой памяти" — IntPtr (аналог PVOID) и функции выделения / освобождения этой памяти и копирования объекта в неуправляемую памяти и обратно. Если проводить аналогию с C++ / C, то для досижения того же эффекта, что и в примере выше, я сделал следующее:
PVOID zeroPtr = 0;
PVOID ppSid = malloc(sizeof(PVOID));        // Marshal.AllocHGlobal
copyObjectToBuffer(zeroPtr, ppSid);         // Marshal.StructureToPtr
func(ppSid);
PVOID pSid = copyObjectFromBuffer(ppSid);   // Marshal.PtrToStruture
free(ppSid);                                // Marshal.FreeHGlobal
// используем pSid



PD>А вообще-то есть


PD>http://pinvoke.net/default.aspx/advapi32/GetNamedSecurityInfo.html


Там аналог решения в лоб, я объяснил, чем оно не устраивает. Когда параметр объявлен как "out IntPtr" передать в него null или IntPtr.Zero не получится.
Компьютер сделает всё, что вы ему скажете, но это может сильно отличаться от того, что вы имели в виду.
Re[3]: Как правильно замаршалить ptr-to-ptr, принимающий NUL
От: Mr. None Россия http://mrnone.blogspot.com
Дата: 17.02.11 18:15
Оценка: 1 (1)
Здравствуйте, sergey_shandar, Вы писали:

_>Здравствуйте, Pavel Dvorkin, Вы писали:


PD>>Здравствуйте, Mr. None, Вы писали:


_>Похоже что так:

_>
_>[DllImport("kernel32.dll", SetLastError=true)]
_>static extern IntPtr LocalFree(IntPtr hMem);

_>[DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
_>internal static extern uint
_>GetNamedSecurityInfo(
_>    string pObjectName,
_>    SE_OBJECT_TYPE objectType,
_>    SECURITY_INFORMATION securityInfo,

_>    [MarshalAs(UnmanagedType.LPArray, SizeConst = 1)]
_>    IntPtr[] pSidOwner,

_>    [MarshalAs(UnmanagedType.LPArray, SizeConst = 1)]
_>    IntPtr[] pSidGroup,

_>    [MarshalAs(UnmanagedType.LPArray, SizeConst = 1)]
_>    IntPtr[] pDacl,

_>    [MarshalAs(UnmanagedType.LPArray, SizeConst = 1)]
_>    IntPtr[] pSacl,

_>    [MarshalAs(UnmanagedType.LPArray, SizeConst = 1)]
_>    IntPtr[] pSecurityDescriptor);
_>


_>Причем, после использования pSecurityDescriptor[0] нужно вызвать LocalFree(pSecurityDescriptor[0]). Остальные указатели считать смещениями: SidOwnerOffset = pSidOwner[0] — pSecurityDescriptor[0], ...


Ок, раз пошла такая пьянка, то объясняю до конца. pSecurityDescriptor не может принимать NULL, то есть он не опциональный, стало быть нет надобности передавать его как-то по особому и сгодиться обычный "ref IntPtr". Поэтому вот достаточная декларация метода:

[DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
internal static extern uint
GetNamedSecurityInfo(
    string pObjectName,
    SE_OBJECT_TYPE objectType,
    SECURITY_INFORMATION securityInfo,

    [MarshalAs(UnmanagedType.LPArray, SizeConst = 1)]
    IntPtr[] pSidOwner,

    [MarshalAs(UnmanagedType.LPArray, SizeConst = 1)]
    IntPtr[] pSidGroup,

    [MarshalAs(UnmanagedType.LPArray, SizeConst = 1)]
    IntPtr[] pDacl,

    [MarshalAs(UnmanagedType.LPArray, SizeConst = 1)]
    IntPtr[] pSacl,

    ref IntPtr pSecurityDescriptor);


И pSecurityDescriptor пережаётся в него самым обычным образом:
IntPtr pSecurityDescriptor;
GetNamedSecurityInfo(..., ref pSecurityDescriptor);


LocalFree в .NET экспозит метод Marshal.FreeHGlobal. Поэтому после использования освободить pSecurityDescriptor можно так:
Marshal.FreeHGlobal(pSecurityDescriptor);
Компьютер сделает всё, что вы ему скажете, но это может сильно отличаться от того, что вы имели в виду.
Re[4]: Как правильно замаршалить ptr-to-ptr, принимающий NUL
От: sergey_shandar США http://getboost.codeplex.com/
Дата: 17.02.11 18:31
Оценка:
Здравствуйте, Mr. None, Вы писали:

MN>Ок, раз пошла такая пьянка, то объясняю до конца. pSecurityDescriptor не может принимать NULL, то есть он не опциональный, стало быть нет надобности передавать его как-то по особому и сгодиться обычный "ref IntPtr". Поэтому вот достаточная декларация метода:


MN>
MN>[DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
MN>internal static extern uint
MN>GetNamedSecurityInfo(
MN>    string pObjectName,
MN>    SE_OBJECT_TYPE objectType,
MN>    SECURITY_INFORMATION securityInfo,

MN>    [MarshalAs(UnmanagedType.LPArray, SizeConst = 1)]
MN>    IntPtr[] pSidOwner,

MN>    [MarshalAs(UnmanagedType.LPArray, SizeConst = 1)]
MN>    IntPtr[] pSidGroup,

MN>    [MarshalAs(UnmanagedType.LPArray, SizeConst = 1)]
MN>    IntPtr[] pDacl,

MN>    [MarshalAs(UnmanagedType.LPArray, SizeConst = 1)]
MN>    IntPtr[] pSacl,

MN>    ref IntPtr pSecurityDescriptor);
MN>


MN>И pSecurityDescriptor пережаётся в него самым обычным образом:

MN>
MN>IntPtr pSecurityDescriptor;
MN>GetNamedSecurityInfo(..., ref pSecurityDescriptor);
MN>


Меня смутила эта дока где последний параметр обозначен как __out_opt.

MN>LocalFree в .NET экспозит метод Marshal.FreeHGlobal. Поэтому после использования освободить pSecurityDescriptor можно так:

MN>
MN>Marshal.FreeHGlobal(pSecurityDescriptor);
MN>


Согласен, главное перед FreeHGlobal(pSecurityDescriptor) отмаршалить все нужные структуры pSecurityDescriptor, pSidOwner[0], pSidGroup[0], pDacl[0], pSacl[0].

В любом случае, самое интересное в твоем посте именно MarshalAs(UnmanagedType.LPArray, SizeConst = 1)] для out opt. Так как таких функций очень много в Windows API. А конкретно эта функция меня мало интересует.
getboost.codeplex.com
citylizard.codeplex.com
Re[3]: Как правильно замаршалить ptr-to-ptr, принимающий NUL
От: Pavel Dvorkin Россия  
Дата: 18.02.11 07:07
Оценка:
Здравствуйте, Mr. None, Вы писали:

MN>Проблема в том, что в C# у нас нет ни указателей, ни операции взятия адреса. У нас есть всего лишь тип "адрес блока неуправляемой памяти" — IntPtr (аналог PVOID) и функции выделения / освобождения этой памяти и копирования объекта в неуправляемую памяти и обратно.


А не проще ли банально перейти в unsafe, и будут и указатели, и взятие адреса. А потом можно написать враппер. Бояться unsafe нечего — мы тут просто вызываем GetNamedSecurityInfo, а она уж точно unsafe

MN>Там аналог решения в лоб, я объяснил, чем оно не устраивает. Когда параметр объявлен как "out IntPtr" передать в него null или IntPtr.Zero не получится.


Там — не получится, а вот так — можно.


using System;
using System.Text;
using System.Runtime.InteropServices;

namespace ConsoleApplication15
{
    class Program
    {
        [DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
        private unsafe static extern uint GetNamedSecurityInfo(String pObjectName, SE_OBJECT_TYPE ObjectType, SECURITY_INFORMATION SecurityInfo, void** pSidOwner, void** pSidGroup, void** pDacl, void** pSacl, void** pSecurityDescriptor);

        [DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
         private unsafe static extern bool LookupAccountSid(String lpSystemName, void* pSid, System.Text.StringBuilder lpName, ref int cchName, System.Text.StringBuilder ReferencedDomainName, ref int cchReferencedDomainName, out int peUse);

private enum SE_OBJECT_TYPE 
{
    SE_UNKNOWN_OBJECT_TYPE=0,     
    SE_FILE_OBJECT, 
    SE_SERVICE, 
    SE_PRINTER,
    SE_REGISTRY_KEY,
    SE_LMSHARE,
    SE_KERNEL_OBJECT,
    SE_WINDOW_OBJECT,
    SE_DS_OBJECT,
    SE_DS_OBJECT_ALL,
    SE_PROVIDER_DEFINED_OBJECT,
    SE_WMIGUID_OBJECT,SE_REGISTRY_WOW64_32KEY
}

[Flags] private enum SECURITY_INFORMATION : uint 
{ 
    Owner = 0x00000001,
    Group = 0x00000002,
    Dacl = 0x00000004,
    Sacl = 0x00000008,
    ProtectedDacl = 0x80000000,
    ProtectedSacl = 0x40000000,
    UnprotectedDacl = 0x20000000,
    UnprotectedSacl = 0x10000000 
}

public static unsafe string GetFileOrDirectoryOwner(String objectName)
    {

        void* psd, pSidOwner, pSidGroup, pDAcl, pAcl ;
        SECURITY_INFORMATION sFlags = SECURITY_INFORMATION.Owner;

        uint errorReturn = GetNamedSecurityInfo(objectName, SE_OBJECT_TYPE.SE_FILE_OBJECT, sFlags, 
            &pSidOwner, // его получить хочу
            (void**)0, (void**)0, (void**)0 , // а эти нет
            &psd);

        if (errorReturn != 0)
        {
        throw(new Exception("An error of code: "+errorReturn+" has occured"));

        }

        int bufferSize = 64;
        int accounLength = bufferSize;
        int domainLength = bufferSize;
        int sidNameUse = 0;

        StringBuilder account = new StringBuilder(bufferSize);
        StringBuilder domain = new StringBuilder(bufferSize);

        if (!LookupAccountSid(null, pSidOwner, account, ref accounLength, domain, ref domainLength, out sidNameUse))
        {
        throw (Marshal.GetExceptionForHR(Marshal.GetHRForLastWin32Error()));        
        }

        return domain+@"\"+account;       
    }

        static void Main(string[] args)
        {
            string s = GetFileOrDirectoryOwner("C:\\Program Files");
            Console.WriteLine(s);
        }
    }
}
With best regards
Pavel Dvorkin
Re[4]: Как правильно замаршалить ptr-to-ptr, принимающий NUL
От: Mr. None Россия http://mrnone.blogspot.com
Дата: 19.02.11 17:14
Оценка: +1
Здравствуйте, Pavel Dvorkin, Вы писали:

PD>Здравствуйте, Mr. None, Вы писали:


MN>>Проблема в том, что в C# у нас нет ни указателей, ни операции взятия адреса. У нас есть всего лишь тип "адрес блока неуправляемой памяти" — IntPtr (аналог PVOID) и функции выделения / освобождения этой памяти и копирования объекта в неуправляемую памяти и обратно.


PD>А не проще ли банально перейти в unsafe, и будут и указатели, и взятие адреса. А потом можно написать враппер. Бояться unsafe нечего — мы тут просто вызываем GetNamedSecurityInfo, а она уж точно unsafe


Да кто ж его боиться-то, я всю свою сознательную жизнь на C++ писал. Просто у меня есть подозрения, что unsafe код далеко не всегда разрешён на уровне системы. И приложение может отказаться работать в определённых окружениях без специальных настроект безопасности. А вот как раз со спецификой .NET безопасности я к сожалению знаком плохо. Подтверждение, а следовательно и разъяснения как эти настройки включить, ровно как и опровержение этого я не нашёл. Но чует моё сердце, что далеко не всегда вот так запросто удасться запустить приложение с unsafe инструкциями. Буду благодарен, если вы развеете мои сомнения. В частности конкретно в моём случае этот код должен исполняться внутри Web-сервиса, работающего под 7-ым IIS`ом на Windows Server 2008.
Компьютер сделает всё, что вы ему скажете, но это может сильно отличаться от того, что вы имели в виду.
Re[5]: Как правильно замаршалить ptr-to-ptr, принимающий NUL
От: Pavel Dvorkin Россия  
Дата: 20.02.11 05:14
Оценка:
Здравствуйте, Mr. None, Вы писали:

MN>Да кто ж его боиться-то, я всю свою сознательную жизнь на C++ писал. Просто у меня есть подозрения, что unsafe код далеко не всегда разрешён на уровне системы. И приложение может отказаться работать в определённых окружениях без специальных настроект безопасности. А вот как раз со спецификой .NET безопасности я к сожалению знаком плохо. Подтверждение, а следовательно и разъяснения как эти настройки включить, ровно как и опровержение этого я не нашёл. Но чует моё сердце, что далеко не всегда вот так запросто удасться запустить приложение с unsafe инструкциями. Буду благодарен, если вы развеете мои сомнения. В частности конкретно в моём случае этот код должен исполняться внутри Web-сервиса, работающего под 7-ым IIS`ом на Windows Server 2008.


Нет, не развею. Я тоже пишу на С++, дотнет для меня не более чем второстепенная область. Ваши подозрения ИМХО небезосновательны, но верны они или нет — пусть другие скажут, те, кто лучше меня в дотнете разбираются.
With best regards
Pavel Dvorkin
Re[5]: Как правильно замаршалить ptr-to-ptr, принимающий NUL
От: Jolly Roger  
Дата: 20.02.11 08:57
Оценка:
Здравствуйте, sergey_shandar, Вы писали:

_>В любом случае, самое интересное в твоем посте именно MarshalAs(UnmanagedType.LPArray, SizeConst = 1)] для out opt. Так как таких функций очень много в Windows API.


Как правило, в этом мало смысла. Managed коду малоинтересны IntPtr и прочие указатели, ему нужны Managed объекты. Потому нативный вызов лучше спрятать в привате, а в публичный доступ предоставить метод, возвращающий управляемые объекты. А в таком случае собственно объявление импортируемой функции труда не представляет. Например, для GetNamedSecurityInfo интереснее вернуть RawSecurityDescriptor, при этом параметры pOwner, pGroup etc уже не важны, так как они просто ссылаются на внутренности дескриптора. Можно, конечно, преобразовать их в SecurityIdentifier, например, но смысла в этом нет.

А добиться желаемого можно методом-обёрткой, в котором либо непосредственно делать преобразования, либо поручить их кастомному маршалеру, вот так, например

  Скрытый текст
    internal class RawSdMarshaler : ICustomMarshaler
    {
        [DllImport("advapi32.dll", SetLastError = true)]
        private static extern Int32 GetSecurityDescriptorLength(IntPtr sd);
        [DllImport("kernel32.dll", SetLastError = true)]
        private static extern IntPtr LocalFree(IntPtr handle);

        public void CleanUpManagedData(object ManagedObj) { }
        public void CleanUpNativeData(IntPtr pNativeData)
        {
            if (pNativeData != IntPtr.Zero) LocalFree(pNativeData);
        }
        public int GetNativeDataSize() { return 0; }
        public IntPtr MarshalManagedToNative(object ManagedObj)
        {
            if (ManagedObj == null) return IntPtr.Zero;
            var sd = (RawSecurityDescriptor)ManagedObj;
            var sz = sd.BinaryLength;
            var buf = new byte[sz];
            sd.GetBinaryForm(buf, 0);
            var ptr = Marshal.AllocHGlobal(sz);
            Marshal.Copy(buf, 0, ptr, sz);
            return ptr;
        }
        public object MarshalNativeToManaged(IntPtr pNativeData)
        {
            if (pNativeData == IntPtr.Zero) return null;
            var sz = GetSecurityDescriptorLength(pNativeData);
            if (sz <= 0) return null;
            var binSd = new byte[sz];
            Marshal.Copy(pNativeData, binSd, 0, sz);
            return new RawSecurityDescriptor(binSd, 0);
        }

        public static ICustomMarshaler GetInstance(string cookie)
        {
            return new RawSdMarshaler();
        }
    }

    public class NativeSecurityInfo
    {
        [Flags]
        private enum QuerySecInfos : uint
        {
            Owner = 0x00000001,
            Group = 0x00000002,
            Dacl = 0x00000004,
            Sacl = 0x00000008,
            All = 0x0000000F
        }

        [DllImport("advapi32.dll", CharSet = CharSet.Unicode)]
        private static extern Int32 GetNamedSecurityInfo(
            string ObjectName, 
            ResourceType ObjectType, QuerySecInfos SecurityInfo,
            IntPtr Owner, IntPtr Group, IntPtr Dacl, IntPtr Sacl,
            [MarshalAs(UnmanagedType.CustomMarshaler, 
                MarshalTypeRef = typeof(RawSdMarshaler))]
            out RawSecurityDescriptor SecurityDescriptor);

        public static RawSecurityDescriptor GetNamedSecurity(
            string ObjectName, ResourceType ObjectType, bool IncludeSacl)
        {
            RawSecurityDescriptor sd = null;
            var flags = 
                QuerySecInfos.Dacl | QuerySecInfos.Group | QuerySecInfos.Owner;
            if (IncludeSacl) flags |= QuerySecInfos.Sacl;
            var r = GetNamedSecurityInfo(
                ObjectName, ObjectType, flags,
                IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, out sd);
            if (r != 0) throw new Win32Exception(r);
            return sd;
        }
    }


Но это так, для развлечения Потому, что в составе фреймворка уже есть класс NativeObjectSecurity и куча потомков от него. А если каких нет, так лучше один раз написать и потом спокойно использовать. Если хотите, могу и его пример выложить.
"Нормальные герои всегда идут в обход!"
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.