2013-08-08 26 views
7

Dlaczego poniższy kod nie działa?Dlaczego to wywołanie AddDllDirectory kończy się niepowodzeniem z "Parametr jest niepoprawny"?

open System 
open System.Runtime.InteropServices 
open System.ComponentModel 

[<DllImport("kernel32")>] 
extern int AddDllDirectory(string NewDirectory) 

[<EntryPoint>] 
let main argv = 
    let result = AddDllDirectory("c:\\") 
    if result = 0 then 
     printfn "%A" <| Win32Exception(Marshal.GetLastWin32Error()) 
     // Prints: "System.ComponentModel.Win32Exception (0x80004005): The parameter is incorrect" 
    System.Console.ReadLine() |> ignore 
    0 // return an integer exit code 

Odpowiedz

15

AddDllDirectory() jest bardzo ostatni dodatek do winapi. Gwarantuje się, że będzie on dostępny tylko w systemie Windows 8, a wcześniejsze wersje systemu Windows wymagają aktualizacji KB2533623. Zachowaj spokój, gdy wybierzesz wymagania dotyczące produktu.

Jest nietypowy na więcej niż jeden sposób, nie podąża za normalnymi wzorami funkcji winapi, które akceptują ciąg znaków. Co czyni tę funkcję dostępną w dwóch wersjach, wersją ANSI z dołączonym A i wersją Unicode z dołączonym W. AddDllDirectory() nie ma dołączonej litery, istnieje tylko wersja Unicode. Nie jest dla mnie jasne, czy było to celowe czy też niedopatrzenie, z wysokimi szansami na celowe działanie. Brak deklaracji funkcji w nagłówkach SDK systemu Windows 8, co jest bardzo nietypowe.

Twoja oryginalna deklaracja nie powiodła się, ponieważ zadzwoniłeś do wersji Unicode, ale osoba przekazująca pinvoke przekazała ciąg ANSI. Prawdopodobnie masz szczęście, ponieważ ciąg ma nieparzystą liczbę znaków z wystarczająco szczęśliwymi zerami, aby nie spowodować naruszenia praw dostępu.

Używanie właściwości CharSet w deklaracji [DllImport] jest wymagane, aby marshaller pinvoke przekazał ciąg znaków Unicode.

2

Po kilku eksperymentach, wydaje się następujące prace:

open System 
open System.Runtime.InteropServices 
open System.ComponentModel 

[<DllImport("kernel32")>] 
extern int AddDllDirectory([<MarshalAs(UnmanagedType.LPWStr)>]string NewDirectory) 

[<EntryPoint>] 
let main argv = 
    let result = AddDllDirectory("c:\\Zorrillo") 
    if result = 0 then 
     printfn "%A" <| Win32Exception(Marshal.GetLastWin32Error()) 
    else 
     printfn "%s" "Woohoo!" 
    System.Console.ReadLine() |> ignore 
    0 // return an integer exit code 
7

Musisz określić, że Unicode jest używany w atrybucie dllimport,

[<DllImport("kernel32", CharSet=CharSet.Unicode)>] 
extern int AddDllDirectory(string NewDirectory) 
+0

Ah, to dlatego muszę określić niezarządzanego typu ciąg, jeśli nie ustawić CharSet? Nawet niejasno zaczyna mieć sens. Powiedziawszy to, myślałem, że Unicode był domyślny? – mavnn

+0

Nie, ANSI jest ustawieniem domyślnym (http://msdn.microsoft.com/en-us/library/7b93s42f.aspx), co jest dość dziwne, ponieważ używasz go tylko tam, gdzie absolutnie musisz (np. GetProcAddress). –