2010-07-06 15 views
6

Funkcje takie jak CreateProcess mają podpisy pobierające wskaźniki do struktur. W C chciałbym po prostu przekazać NULL jako wskaźnik dla opcjonalnych parametrów, zamiast tworzyć fikcyjny obiekt struct na stosie i przekazywać wskaźnik do manekina.P/wywołanie funkcji przyjmującej wskaźnik do struktury

W języku C#, mam zadeklarowane jako (P/Invoke)

[DllImport("kernel32.dll", CharSet = CharSet.Auto)] 
     public static extern bool CreateProcess(
      string lpApplicationName, 
      string lpCommandLine, 
      ref SECURITY_ATTRIBUTES lpProcessAttributes, 
      ref SECURITY_ATTRIBUTES lpThreadAttributes, 
      bool bInheritHandles, 
      CreateProcessFlags dwProcessCreationFlags, 
      IntPtr lpEnvironment, 
      string lpCurrentDirectory, 
      ref STARTUPINFO lpStartupInfo, 
      ref PROCESS_INFORMATION lpProcessInformation); 

Ale gdy próbuję przekazać null dla argumentu lpProcessAttributes lub lpThreadAttributes argumentu, otrzymuję błąd kompilatora:

Error 2 Argument 3: cannot convert from '<null>' to 'ref Debugging.Wrappers.SECURITY_ATTRIBUTES'

Jak mogę zmodyfikować powyższy podpis funkcji, aby móc po prostu przekazać null dla argumentów SECURITY_ATTRIBUTES, bez błędu kompilatora? (A także być w stanie przekazać prawdziwą strukturę, jeśli chcę?)

+0

Wygląda na to, że moje pytanie jest takie samo jak to, chociaż nie zgadłbym tego z tytułu. http://stackoverflow.com/questions/1049623/how-to-pass-a-ululowane-type-to-a-p-invoked-function –

Odpowiedz

3

null jest poprawna tylko dla typów odniesienia w .Net. twoje SECURITY_ATTRIBUTES to struct, czyli ValueType. Zamiast przekazywania wartości null, musisz przekazać pustą strukturę SECURITY_ATTRIBUTES. (po prostu powiedz new SECURITY_ATTRIBUTES()) w swoim telefonie.

Czystsze metoda jest dodanie statycznej Empty własności do swojej struktury, a po prostu przekazać SECURITY_ATTRIBUTES.Empty

[StructLayout(LayoutKind.Sequential)] 
public struct SECURITY_ATTRIBUTES { 
    public int nLength; 
    public IntPtr lpSecurityDescriptor; 
    public int bInheritHandle; 

    public static SECURITY_ATTRIBUTES Empty { 
     get { 
      return new SECURITY_ATTRIBUTES { 
       nLength = sizeof(int)*2 + IntPtr.Size, 
       lpSecurityDescriptor = IntPtr.Zero, 
       bInheritHandle = 0, 
      }; 
     } 
    } 
} 

albo jeszcze lepiej, niż przy użyciu P/Invoke do tworzenia procesu, sprawdź klasę System.Diagnostics.Process, która prawdopodobnie powinna zrobić to, czego potrzebujesz. (!)

+0

Dziękuję, podoba mi się ten BEZPIECZNIK_ATTRZYMALIZACJI.Opróżniający wzór. –

+0

Swoją drogą użyłem p/invoke do utworzenia procesu, ponieważ chcę robić rzeczy z uprawnieniami procesowymi, które nie będą wspierane przez zarządzane interfejsy API. Ale poza tym tak, użyłbym klasy System.Diagnostics.Process. –

+0

Ale problem z właściwością Empty polega na tym, że nie można go przekazać jako parametru ref. Biorąc pod uwagę, że wiele interfejsów API Win32 zajmujących się strukturami oczekuje adresu, potrzebna jest poprawka. – Adarsha

6

OK, w końcu znalazłem jeszcze lepszy sposób to zrobić:

Declare SECURITY_ATTRIBUTES jako klasy zamiast struct i nie należy go przekazywać przez ref. :-)

[DllImport("kernel32.dll", SetLastError = true)] 
    public static extern bool CreateProcess(
     string lpApplicationName, 
     StringBuilder lpCommandLine, 
     SECURITY_ATTRIBUTES lpProcessAttributes, 
     SECURITY_ATTRIBUTES lpThreadAttributes, 
     bool bInheritHandles, 
     CreateProcessFlags dwCreationFlags, 
     IntPtr lpEnvironment, 
     string lpCurrentDirectory, 
     STARTUPINFO lpStartupInfo, /// Required 
     PROCESS_INFORMATION lpProcessInformation //Returns information about the created process 
     ); 

/// <summary> 
/// See http://msdn.microsoft.com/en-us/library/aa379560(v=VS.85).aspx 
/// </summary> 
[StructLayout(LayoutKind.Sequential)] 
public class SECURITY_ATTRIBUTES 
{ 
    public uint nLength; 
    public IntPtr lpSecurityDescriptor; 
    [MarshalAs(UnmanagedType.Bool)] public bool bInheritHandle; 
} 

Bonus: to pozwala także zadeklarować godnej konstruktora na SECURITY_ATTRIBUTES który inicjuje nLength.

+0

Działa bez względu na to, do którego nazwę zmienisz klasę. Świetna odpowiedź! –

+1

Zastanawiam się jednak, czy to jest bezpieczne. Atrybut 'StructLayout' może dotyczyć tylko struktur, a nie klas. Być może JIT .NET może rozmieścić pola w tej klasie w dowolny sposób. Na przykład, zastanawiam się, czy to może zawieść na x64 lub na innych platformach. Nie mogę być bezpieczną sztuczką. –

+0

Po prostu usłyszałem od wewnętrznego eksperta CLR z MSFT, że jest to bezpieczna sztuczka. :) –

Powiązane problemy