mavnn
mavnn

Reputation: 9469

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

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

Upvotes: 7

Views: 7470

Answers (3)

Hans Passant
Hans Passant

Reputation: 941545

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.

Upvotes: 17

John Reynolds
John Reynolds

Reputation: 5057

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

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

Upvotes: 8

mavnn
mavnn

Reputation: 9469

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

Upvotes: 2

Related Questions