IT rekvalifikace s garancí práce. Seniorní programátoři vydělávají až 160 000 Kč/měsíc a rekvalifikace je prvním krokem. Zjisti, jak na to!
Hledáme nové posily do ITnetwork týmu. Podívej se na volné pozice a přidej se do nejagilnější firmy na trhu - Více informací.

Použití Windows API v C# .NET - 4. díl

Doposud jsme řešili implementaci API do programu spíše intuicí a v řadě případů zavádíme do programu chyby, které se velmi těžko hledají. Pokud nahlédneme do implementace API do .NET Frameworku, zjistíme, že zhruba 20% API je řešeno pomocí ukazatelů (pointerů) a tak zvaným nebezpečným kódem (unsafe). Důvod je prostý, lepší optimalizace a rychlost programu. V dalších tutoriálech se naučíme jak programovat tento unsafe kód. Pro lepší efektivitu naší práce doinstalujeme do Visual Studia balíček PInvoke.net, který nám velice usnadní práci.

PInvoke.net

Instalace balíčku přes správce rozšíření je velmi jednoduchá a myslím si, že není nutný další popis.

Instalace rozšíření PInvoke.net do Visual Studia - Programování služeb ve Windows

Pokud zadáme například API ReadFile(), jsou nám nabídnuty čtyři varianty v C# a dvě varianty pro VB.NET:

PInvoke_2 - Programování služeb ve Windows

Varianta dvě a čtyři používá nové klíčové slovo unsafe a to znamená, že funkce bude používat nebezpečný kód a bude využívat ukazatele. Také příklad s uvedenou API používá nebezpečný kód.

public class Class1
{
    //1
    [DllImport("kernel32.dll", SetLastError = true)]
    static extern bool ReadFile(IntPtr hFile, [Out] byte[] lpBuffer,
    uint nNumberOfBytesToRead, out uint lpNumberOfBytesRead, IntPtr lpOverlapped);

    //2
    [DllImport("kernel32.dll", SetLastError = true)]
    static extern unsafe int ReadFile(IntPtr handle, IntPtr bytes, uint numBytesToRead,
    IntPtr numBytesRead, NativeOverlapped* overlapped);

    //3
    [DllImport("kernel32.dll", SetLastError=true)]
    static extern bool ReadFile(IntPtr hFile, [Out] byte[] lpBuffer, uint nNumberOfBytesToRead,
    out uint lpNumberOfBytesRead, [In] ref System.Threading.NativeOverlapped lpOverlapped);

    //4
    [DllImport(@"kernel32.dll", SetLastError = true)]
    static extern unsafe bool ReadFile(
    SafeFileHandle hFile,       // handle to file
    byte* pBuffer,              // data buffer, should be fixed
    int NumberOfBytesToRead,    // number of bytes to read
    IntPtr pNumberOfBytesRead,  // number of bytes read, provide IntPtr.Zero here
    NativeOverlapped *lpOverlapped // should be fixed, if not IntPtr.Zero
    );

    // This is taken from the USBSharp.cs class
    public unsafe byte[] CT_ReadFile(int InputReportByteLength)
    {
        int BytesRead = 0;
        byte[] BufBytes = new byte[InputReportByteLength];
        if (ReadFile(HidHandle, BufBytes, InputReportByteLength, ref BytesRead, null))
        {
            byte[] OutBytes = new byte[BytesRead];
            Array.Copy(BufBytes, OutBytes, BytesRead);
            return OutBytes;
        }
        else
        {
            return null;
        }
    }
}

Velkou výhodou je automatické načtení kódu do Visual Studia.

Nahlédnutí pod pokličku .NET frameworku

Podívejme se pod pokličku .NET frameworku. Nejprve provedeme dekompilaci například System.Core:

// Type: System.IO.Pipes.AnonymousPipeServerStream
// Assembly: System.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
// MVID: BA37026F-1371-4849-854A-E2CCE7CAD02B
// Assembly location: C:\Windows\Microsoft.NET\Framework\v4.0.30319\System.Core.dll

using Microsoft.Win32.SafeHandles;
using System;
using System.IO;
using System.Runtime;
using System.Runtime.InteropServices;
using System.Security;
using System.Security.Permissions;

namespace System.IO.Pipes
{
    [HostProtection(SecurityAction.LinkDemand, MayLeakOnAbort = true)]
    public sealed class AnonymousPipeServerStream : PipeStream
    {
        private SafePipeHandle m_clientHandle;
        private bool m_clientHandleExposed;

    //nějaký kód


    [SecurityCritical]
        private void Create(PipeDirection direction, Microsoft.Win32.UnsafeNativeMethods.SECURITY_ATTRIBUTES
        secAttrs, int bufferSize)
        {
            SafePipeHandle hSourceHandle;
            if (!(direction != PipeDirection.In ? Microsoft.Win32.UnsafeNativeMethods.CreatePipe(out
        this.m_clientHandle, out hSourceHandle, secAttrs, bufferSize) :
           Microsoft.Win32.UnsafeNativeMethods.CreatePipe(out hSourceHandle, out this.m_clientHandle,
         secAttrs, bufferSize)))
                __Error.WinIOError(Marshal.GetLastWin32Error(), string.Empty);
            SafePipeHandle lpTargetHandle;
    if(!Microsoft.Win32.UnsafeNativeMethods.DuplicateHandle(
    Microsoft.Win32.UnsafeNativeMethods.GetCurrentProcess(), hSourceHandle,
    Microsoft.Win32.UnsafeNativeMethods.GetCurrentProcess(), out lpTargetHandle, 0U, false, 2U))
                __Error.WinIOError(Marshal.GetLastWin32Error(), string.Empty);
            hSourceHandle.Dispose();
            this.InitializeHandle(lpTargetHandle, false, false);
            this.State = PipeState.Connected;
        }
    }
}

Zde vidíme neznámé using Microsoft.Win32­.UnsafeNative­Methods. Pokud tuto třídu dekompilujeme:

// Type: Microsoft.Win32.UnsafeNativeMethods
// Assembly: System.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
// MVID: BA37026F-1371-4849-854A-E2CCE7CAD02B
// Assembly location: C:\Windows\Microsoft.NET\Framework\v4.0.30319\System.Core.dll

using Microsoft.Win32.SafeHandles;
using System;
using System.Diagnostics.Eventing;
using System.Diagnostics.Eventing.Reader;
using System.IO;
using System.Runtime.ConstrainedExecution;
using System.Runtime.InteropServices;
using System.Security;
using System.Text;
using System.Threading;

namespace Microsoft.Win32
{
    internal static readonly IntPtr NULL = IntPtr.Zero;
    internal const string KERNEL32 = "kernel32.dll";
    internal const string ADVAPI32 = "advapi32.dll";
    internal const string WEVTAPI = "wevtapi.dll";
    internal const int CREDUI_MAX_USERNAME_LENGTH = 513;
    internal const int ERROR_SUCCESS = 0;
    internal const int ERROR_FILE_NOT_FOUND = 2;
    internal const int ERROR_PATH_NOT_FOUND = 3;
    internal const int ERROR_ACCESS_DENIED = 5;

    //pokračují další konstanty

    [SecurityCritical]
    [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
    [DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
    [return: MarshalAs(UnmanagedType.Bool)]
    internal static extern bool FreeLibrary(IntPtr hModule);

    [SecurityCritical]
    [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
    [DllImport("advapi32.dll", SetLastError = true)]
    [return: MarshalAs(UnmanagedType.Bool)]
    internal static extern bool RevertToSelf();

    [SecurityCritical]
    [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
    [DllImport("advapi32.dll", SetLastError = true)]
    [return: MarshalAs(UnmanagedType.Bool)]
    internal static extern bool ImpersonateNamedPipeClient(SafePipeHandle hNamedPipe);

    //Další funkce API použité v System.Core

Důležitý poznatek tedy je, že vývojáři .NET frameworku mají k dispozici neuvedené třídy, které obsahují Pinvoke všech API funkcí.

Importované funkce mají dodané atributy, pokusím se je ve stručnosti popsat.

[HostProtecti­on(SecurityAc­tion.LinkDeman­d, MayLeakOnAbort = true)]

  • HostProtectio­nAttribute - Tato třída umožňuje používat deklarované zabezpečení akce a určit požadavky na ochranu hostitele.
  • SecurityAction­.LinkDemand (podporováno rozhraním XNA) - Nepoužívat v .NET Framework 4. Přidělí oprávnění volajícímu.
  • MayLeakOnAbort = true - Kód ukončení může způsobit únik paměti, pokud nejsou chráněna bezpečným popisovačem nebo není jiným způsobem zajištěno uvolnění paměti.

[SecurityCriti­cal]

  • Je ekvivalentní pro odkaz na úplný vztah důvěryhodnosti.
  • Typ nebo člena označeného SecurityCriti­calAttribute lze volat pouze prostřednictvím plně důvěryhodného kódu. Nemůže být volán z částečně důvěryhodného kódu.

[PermissionSet(Se­curityAction.De­mand, Name = "FullTrust")]

  • Kód může volat držitel speciálního oprávnění.

SecuritySafeCri­tical

  • Typy nebo členy označené SecuritySafeCri­ticalAttribute - Atribut pro přístup k částečně důvěryhodnému typu a členu.

Pro milovníky dobrého kódu je zajímavé uvolňování alokované paměti ve třídě System.IO.Pipes

Ukázka překladu:

Překlad - Programování služeb ve Windows

Závěr

Na závěr jsem do zdrojových kódů přidal celou třídu class UnsafeNativeMet­hods a několik tříd, které ji volají. Pro milovníky kódu je to velmi poučná pasáž, ukázka řeší jak programují profesionálové. Tím ale nemyslím sebe. V dalších částech popíši nebezpečný kód založený na použití pointerů a uvedu několik příkladů. Příjemné počtení.


 

Stáhnout

Stažením následujícího souboru souhlasíš s licenčními podmínkami

Staženo 72x (17.08 kB)
Aplikace je včetně zdrojových kódů v jazyce C#

 

Všechny články v sekci
Programování služeb ve Windows
Článek pro vás napsal zpavlu
Avatar
Uživatelské hodnocení:
Ještě nikdo nehodnotil, buď první!
C# , C++ a assembler
Aktivity