Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does this call to AddDllDirectory fail with "Parameter is incorrect"?

Tags:

.net

pinvoke

f#

Why does the following code not work?

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
like image 606
mavnn Avatar asked Aug 08 '13 13:08

mavnn


3 Answers

AddDllDirectory() is a very recent addition to the winapi. It is only guaranteed to be available in Windows 8, getting it on earlier Windows versions requires an update, KB2533623. Do keep this is mind when you select your product requirements.

It is unusual in more than one way, it doesn't follow the normal pattern for winapi functions that accept a string. Which makes the function available in two versions, the ANSI version that has an A appended and the Unicode version that has a W appended. AddDllDirectory() has no appended letter, only the Unicode version exists. It isn't clear to me whether that was intentional or an oversight, with high odds for intentional. The function declaration is missing from the Windows 8 SDK headers, very unusual indeed.

So your original declaration failed because you called the Unicode version but the pinvoke marshaller passed an ANSI string. You probably got lucky because the string had an odd number of characters with enough lucky zeros to not cause an AccessViolation.

Using the CharSet property in the [DllImport] declaration is required so the pinvoke marshaller passes a Unicode string.

like image 170
Hans Passant Avatar answered Jan 04 '23 11:01

Hans Passant


You need to specify that unicode is used in the DllImport attribute,

[<DllImport("kernel32", CharSet=CharSet.Unicode)>]
extern int AddDllDirectory(string NewDirectory)
like image 24
John Reynolds Avatar answered Jan 04 '23 11:01

John Reynolds


After some experimentation, it appears the following works:

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
like image 20
mavnn Avatar answered Jan 04 '23 12:01

mavnn