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

C# .NET Pro pokročilé 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

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

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

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ženo 52x (17.08 kB)
Aplikace je včetně zdrojových kódů v jazyce C#

 

  Aktivity (1)

Článek pro vás napsal zpavlu
Avatar
C# a C++

Jak se ti líbí článek?
Ještě nikdo nehodnotil, buď první!


 


Miniatura
Všechny články v sekci
C# - Pro pokročilé
Miniatura
Následující článek
TCP připojení v C# .NET

 

 

Komentáře

Děláme co je v našich silách, aby byly zdejší diskuze co nejkvalitnější. Proto do nich také mohou přispívat pouze registrovaní členové. Pro zapojení do diskuze se přihlas. Pokud ještě nemáš účet, zaregistruj se, je to zdarma.

Zatím nikdo nevložil komentář - buď první!