P / Invoke error, or am I doing it wrong?

So, I wrote the following code:

using (var serviceController = new ServiceController(serviceName))
{
    var serviceHandle = serviceController.ServiceHandle;

    using (failureActionsStructure.Lock())
    {
        success = NativeMethods.ChangeServiceConfig2W(
            serviceHandle,
            ServiceConfigType.SERVICE_CONFIG_FAILURE_ACTIONS,
            ref failureActionsStructure);

        if (!success)
            throw new Win32Exception();
    }
}

The P / Invoke ad is as follows:

[DllImport("advapi32", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern bool ChangeServiceConfig2W(IntPtr hService, ServiceConfigType dwInfoLevel, ref SERVICE_FAILURE_ACTIONSW lpInfo);

ServiceConfigType- it is simple enum, and this particular element has a value of 2. The structure SERVICE_FAILURE_ACTIONSWis defined as follows:

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
struct SERVICE_FAILURE_ACTIONSW
{
    public int dwResetPeriod;
    public string lpRebootMsg;
    public string lpCommand;
    public int cActions;
    public IntPtr lpsaActionsPtr;

    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)]
    public SC_ACTION[] lpsaActions;

    class DataLock : IDisposable
    {
        IntPtr _buffer;

        public DataLock(ref SERVICE_FAILURE_ACTIONSW data)
        {
            int actionStructureSize = Marshal.SizeOf(typeof(SC_ACTION));

            // Allocate a buffer with a bit of extra space at the end, so that if the first byte isn't aligned to a 64-bit
            // boundary, we can simply ignore the first few bytes and find the next 64-bit boundary.
            _buffer = Marshal.AllocHGlobal(data.lpsaActions.Length * actionStructureSize + 8);

            data.lpsaActionsPtr = _buffer;

            // Round up to the next multiple of 8 to get a 64-bit-aligned pointer.
            if ((data.lpsaActionsPtr.ToInt64() & 7) != 0)
            {
                data.lpsaActionsPtr += 8;
                data.lpsaActionsPtr -= (int)((long)data.lpsaActionsPtr & ~7);
            }

            // Copy the data from lpsaActions into the buffer.
            IntPtr elementPtr = data.lpsaActionsPtr;

            for (int i=0; i < data.lpsaActions.Length; i++, elementPtr += actionStructureSize)
                Marshal.StructureToPtr(data.lpsaActions[i], elementPtr, fDeleteOld: false);
        }

        public void Dispose()
        {
            Marshal.FreeHGlobal(_buffer);
        }
    }

    internal IDisposable Lock()
    {
        return new DataLock(ref this);
    }
}

(This type has an additional member at the end of the structure that does not appear in the definition of its own structure, lpsaActionswhich simplifies the use of this structure and only leads to adding additional data at the end - data that the basic API simply ignores, since it assumes that the structure has already completed in mind.)

SC_ACTION defined as follows:

[StructLayout(LayoutKind.Sequential)]
struct SC_ACTION
{
    public SC_ACTION_TYPE Type;
    public int Delay;
}

.. and SC_ACTION_TYPEis simple enum:

enum SC_ACTION_TYPE
{
    SC_ACTION_NONE = 0,
    SC_ACTION_RESTART = 1,
    SC_ACTION_REBOOT = 2,
    SC_ACTION_RUN_COMMAND = 3,
}

The structure I pass is initialized as follows:

var failureActionsStructure =
    new SERVICE_FAILURE_ACTIONSW()
    {
        dwResetPeriod = 60000, // 60 seconds
        lpRebootMsg = "",
        lpCommand = "",
        cActions = 6,
        lpsaActions =
            new SC_ACTION[]
            {
                new SC_ACTION() { Type = SC_ACTION_TYPE.SC_ACTION_RESTART /* 1 */, Delay = 5000 /* 5 seconds */ },
                new SC_ACTION() { Type = SC_ACTION_TYPE.SC_ACTION_RESTART /* 1 */, Delay = 15000 /* 15 seconds */ },
                new SC_ACTION() { Type = SC_ACTION_TYPE.SC_ACTION_RESTART /* 1 */, Delay = 25000 /* 25 seconds */ },
                new SC_ACTION() { Type = SC_ACTION_TYPE.SC_ACTION_RESTART /* 1 */, Delay = 35000 /* 35 seconds */ },
                new SC_ACTION() { Type = SC_ACTION_TYPE.SC_ACTION_RESTART /* 1 */, Delay = 45000 /* 45 seconds */ },
                new SC_ACTION() { Type = SC_ACTION_TYPE.SC_ACTION_NONE /* 0 */, Delay = 0 /* immediate, and this last entry is then repeated indefinitely */ },
            },
    };

64- , . 32- , ( 32- 32- Windows - , 32- 64- Windows), ERROR_INVALID_HANDLE .

. API ChangeServiceConfig2W stdcall, , , :

  • (DWORD PTR)
  • (DWORD PTR)
  • (DWORD) (2)
  • (DWORD PTR)

, 32- # ChangeServiceConfig2W ( _ChangeServiceConfig2WStub@12), , :

  • (DWORD PTR)
  • (DWORD PTR) 0x0000AFC8
  • (DWORD) 0x00000001, 2
  • (DWORD PTR)

++, DWORD off [ESP] 2. P/Invoke, int IntPtr ServiceConfigType , .

, ref SERVICE_FAILURE_ACTIONSW, , IntPtr, failureActionsStruct Marshal.StructureToPtr , Marshal.AllocHGlobal. .

, : - , ChangeServiceConfig2W, , , ? , , ​​P/Invoke ( , ).

, DWORD 0x0000AFC8 - 45 000 Delay SC_ACTION failureActionsStructure. . , 0x00000001, 0x0000AFC8 , Type SC_ACTION. , , , P/Invoke. , , , ?

.: -)

+4
1

, , . , P/Invoke, , Microsoft : " ".

, , . , , UnmanagedType.SafeArray VARIANT, UnmanagedType.ByValArray, SizeConst - , .

Marshal.SizeOf, , , SizeConst * Marshal.SizeOf(arrayElementType). , , .

, , , , SizeConst. , , SizeConst 1, 6 , Marshal.SizeOf, , 6 , .

, , , . , , , , , , , . , , , , .

, , 6- , , ChangeServiceConfig2W - ChangeServiceConfig2W , . , , , , ExecutionEngineException @GSerg.

64- - -, 64 , . , ChangeServiceConfig2W. .

-, ; , ( , SizeConst, !), . , : " , SizeConst 1, 1 ". , , . , .NET , .

, , DataLock, lpsaActions ( ChangeServiceConfig2W, P/Invoke ), lpsaActions 1- . 1 , . DataLock Dispose d, lpsaActions .

() GitHub ++, , ChangeServiceConfig2W :

, , WindowsAPI/SERVICE_FAILURE_ACTIONSW.cs:

            // Replace the lpsaActions array with a dummy that contains only one element, otherwise the P/Invoke marshaller
            // will allocate a buffer of size 1 and then write lpsaActions.Length items to it and corrupt memory.
            _originalActionsArray = data.lpsaActions;

            data.lpsaActions = new SC_ACTION[1];

32- . ( 64- , , , , " 32-", "x86".)

Microsoft . , Microsoft UnmanagedType.ByValArray, - , . , .NET , , , SizeConst .: -)

+1

Source: https://habr.com/ru/post/1653378/


All Articles