I am porting a VB.NET application to F# as an experiment. The VB program uses SQLDriverConnect, so I need to call it from F#. I cannot get the pinvoke/extern declaration to work properly. The call to SQLDriver connect always returns -2, SQL_INVALID_HANDLE, instead of prompting for a connection as expected.
Anybody know how to get this to work?
open System
open System.Runtime.InteropServices
open System.Text
[<DllImport("odbc32.dll")>]
extern Int16 SQLAllocEnv(IntPtr& EnvironmentHandle);
[<DllImport("odbc32.dll")>]
extern Int16 SQLDriverConnect(IntPtr hdbc, IntPtr hwnd, string szConnStrIn,
Int16 cbConnStrIn, StringBuilder szConnStrOut,
Int16 cbConnStrOutMax, Int16& pcbConnStrOut,
UInt16 fDriverCompletion)
let getConnectionString () =
let SQL_DRIVER_PROMPT = 2us
let mutable henv = IntPtr(0)
let mutable csLen = 0s
let rc1 = SQLAllocEnv &henv
assert (rc1 = 0s)
let csOut = new StringBuilder(1024)
let rc2 = SQLDriverConnect(henv, IntPtr.Zero, "", 0s, csOut, 1024s, &csLen, SQL_DRIVER_PROMPT)
assert (rc2 = 0s)
csOut.ToString()
[<EntryPoint>]
let main argv =
printfn "Connection string: %s" (getConnectionString())
0 // return an integer exit code
I don't do F# but in C you call SQLAllocEnv (or SQLAllocHandle) to create an environment handle, then you call SQLSetEnvAttr to set the version of ODBC you want, then SQLAllocConnect (or SQLAllocHandle) to allocate a connection handle and lastly call SQLDriverConnect with the connection handle. Your code looks to be passing an environment handle to SQLDriverConnect but SQLDriverConnect needs a connection handle hence SQL_INVALID_HANDLE.
Related
I have a native C library and I want do some F# coding with it. The thing is I get exception:
System.TypeLoadException: Cannot marshal field 'log' of type
'LoggingModel': There is no marshaling support for this type.
at
System.StubHelpers.ValueClassMarshaler.ConvertToNative(IntPtr dst,
IntPtr src, IntPtr pMT, CleanupWorkList& pCleanupWorkList)
at
FSI_0009.Initialize(ComponentOverrideFlags flags, LoggingModel&
loggingModel, ThreadingModel& threadingModel, SchedulingModel&
schedulingModel, IntPtr memoryModel)
at
.$FSI_0011.main#()
in
D:\dev_p\f#\FunBindings\FunExample\Environment.fs:line 16 Stopped due
to error
Here the code:
module Interop
[<CLSCompliant(true); Flags>]
type LogTarget =
| None = 0
| Console = 1
| Trace = 2
| Custom = 4
[<UnmanagedFunctionPointer(CallingConvention.Cdecl)>]
type LogCallback = delegate of LogTarget * string * string * nativeint -> unit
[<UnmanagedFunctionPointer(CallingConvention.Cdecl)>]
type ReleaseCallback = delegate of nativeint -> unit
[<Struct>]
type LoggingModel =
val mutable targets : LogTarget
val mutable log : LogCallback
val mutable deleteModel : ReleaseCallback
val mutable userparam : IntPtr
[<DllImport("CLIBRARY.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "txInitialize")>]
[<MethodImpl(MethodImplOptions.ForwardRef)>]
extern int Initialize(ComponentOverrideFlags flags, LoggingModel& loggingModel, ThreadingModel& threadingModel, SchedulingModel& schedulingModel, IntPtr memoryModel)
module Environment
let initialize =
let mutable loggingModel = new LoggingModel()
let mutable threadingModel = new ThreadingModel()
let mutable schedulingModel = new SchedulingModel()
Initialize(ComponentOverrideFlags.None, &loggingModel, &threadingModel, &schedulingModel, IntPtr.Zero)
Basically, I get the aforementioned error when I try to execute "initialize" function in interactive.
I would really appreciate any help.
Update: I've checked the code a bit more and noticed that outside of the interactive console it seems to be working, without failing with exceptions. I need to provide a bit more coverage for CLibrary to be sure. Meanwhile, if there anybody who knows what could cause this exception and how it could be prevented, I would really appreciate the answer.
I think the problem is that delegate of LogTarget * string * string * nativeint -> unit declares a delegate where the arguments are curried. (This doesn't really make sense to me either since a * b normally represents a tuple.)
The subtly different delegate of (LogTarget * string * string * nativeint) -> unit declares a delegate with tupled arguments which would be compatible with a native function.
You can see this difference if you try and assign a .NET method to two different delegate types:
type Curried = delegate of int * int -> int
type Tupled = delegate of (int * int) -> int
//let a = new Curried (Math.Max) // doesn't compile
let b = new Tupled (Math.Max) // works
Have you tried adding [<MarshalAsAttribute(UnmanagedType.FunctionPtr)>] to LoggingModel?
[<Struct>]
type LoggingModel =
val mutable targets : LogTarget
[<MarshalAsAttribute(UnmanagedType.FunctionPtr)>]
val mutable log : LogCallback
[<MarshalAsAttribute(UnmanagedType.FunctionPtr)>]
val mutable deleteModel : ReleaseCallback
val mutable userparam : IntPtr
IL code without this attribute is:
// Fields
.field public class Interop.LogCallback log
but with this attribute is:
// Fields
.field public marshal(Func) class Interop.LogCallback log
Without marshal(Func)/MarshalAs attribute the delegate cannot be marshalled even with the UnmanagedFunctionPointer attribute. Cannot test it with a native library though.
This question already has answers here:
How can I pass an F# delegate to a P/Invoke method expecting a function pointer?
(2 answers)
Closed 8 years ago.
I am trying to call EnumWindows from F# and got following exception:
System.Runtime.InteropServices.MarshalDirectiveException: Cannot marshal 'parameter #1': Generic types cannot be marshaled.
Code I used:
module Win32 =
open System
open System.Runtime.InteropServices
type EnumWindowsProc = delegate of (IntPtr * IntPtr) -> bool
[<DllImport("user32.dll")>]
extern bool EnumWindows(EnumWindowsProc callback, IntPtr lParam)
let EnumTopWindows() =
let callback = new EnumWindowsProc(fun (hwnd, lparam) -> true)
EnumWindows(callback, IntPtr.Zero)
module Test =
printfn "%A" (Win32.EnumTopWindows())
This is a bit subtle, but when you use parentheses in the delegate definition, you are explicitly telling the compiler to create a delegate taking tuple - and then the interop fails because it cannot handle tuples. Without parentheses, the delegate is created as ordinary .NET delegate with two parameters:
type EnumWindowsProc = delegate of IntPtr * IntPtr -> bool
Then you also have to change how you use it (because it is now treated as two-parameter function):
let EnumTopWindows() =
let callback = new EnumWindowsProc(fun hwnd lparam -> true)
EnumWindows(callback, IntPtr.Zero)
This question already has answers here:
Call F# code from C#
(4 answers)
Closed 8 years ago.
I am SQL developer and am really new to both F# and C#. I need help on how to pass a string to f# function below and to return the result from F# to C#.
Description of project:
I am using stanford postagger to tag a sentence with the parts of speech.
Reference link from where i copied this code.
(http://sergey-tihon.github.io/Stanford.NLP.NET/StanfordPOSTagger.html)
module File1
open java.io
open java.util
open edu.stanford.nlp.ling
open edu.stanford.nlp.tagger.maxent
// Path to the folder with models
let modelsDirectry =
__SOURCE_DIRECTORY__ + #'..\stanford-postagger-2013-06-20\models\'
// Loading POS Tagger
let tagger = MaxentTagger(modelsDirectry + 'wsj-0-18-bidirectional-nodistsim.tagger')
let tagTexrFromReader (reader:Reader) =
let sentances = MaxentTagger.tokenizeText(reader).toArray()
sentances |> Seq.iter (fun sentence ->
let taggedSentence = tagger.tagSentence(sentence :?> ArrayList)
printfn "%O" (Sentence.listToString(taggedSentence, false))
)
// Text for tagging
let text = System.Console.ReadLine();
tagTexrFromReader <| new StringReader(text)
it won't matter if C# or F# - do make a function that gets a string and returns ... let
s say an int, you just need something like this (put it in some MyModule.fs):
namespace MyNamespace
module MyModule =
// this is your function with one argument (a string named input) and result of int
let myFun (input : string) : int =
// do whatever you have to
5 // the value of the last line will be your result - in this case a integer 5
call it in from C#/.net with
int result = MyNamespace.MyModule.myFun ("Hallo");
I hope this helps you out a bit
For your example this would be:
let myFun (text : string) =
use reader = new StringReader(text)
tagTexrFromReader reader
as you'll have this in the module File1 you can just call it with var res = Fiel1.myFun(text);
BTW: use is in there because StringReader is IDisposable and using use F# will dispose the object when you exit the scope.
PS: is tagTexrFromReader a typo?
I've tried the following code in VS2010:
open System.Security.Cryptography
let rsaTest1 =
let ecKey = [|0uy..143uy|] // junk data for testing
let ecKeyMod = ecKey.[8..8+128-1]
let ecKeyExp = ecKey.[136..136+8-1]
let rsa = RSAParameters(Modulus = ecKeyMod, Exponent = ecKeyExp)
rsa
let rsaTest2 =
let ecKey = [|0uy..143uy|] // junk data for testing
let rsa = RSAParameters(Modulus = ecKey.[8..8+128-1], Exponent = ecKey.[136..136+8-1])
rsa
If I highlight all code and send it to F# Interactive (Alt+Enter), then rsaTest1 works, but rsaTest2 gives an error message,
System.NullReferenceException: Object reference not set to an instance of an object.
at <StartupCode$FSI_0004>.$FSI_0004.main#() in P:\proj\Tachograph\Project\CompuTachTest\CompuTachTest\rsaTest.fsx:line 16
However, if I change rsaTest2 from a value into a function and call it,
let rsaTest2 () =
let ecKey = [|0uy..143uy|] // junk data for testing
let rsa = RSAParameters(Modulus = ecKey.[8..8+128-1], Exponent = ecKey.[136..136+8-1])
rsa
let x = rsaTest2 ()
then there is no error. F# bug or my mistake?
This is most likely a bug - if you compile the posted snippet with fsc and run it, you get this for x64:
Unhandled Exception: System.InvalidProgramException: Common Language Runtime detected an invalid program.
at <StartupCode$test>.$Test.main#()
and this for x86:
Unhandled Exception: System.InvalidProgramException: JIT Compiler encountered an internal limitation.
at <StartupCode$test>.$Test.main#()
You should report it via Microsoft Connect.
I'm running into a bug in my code that makes me think that I don't really understand some of the details about F# and lazy evaluation. I know that F# evaluates eagerly and therefore am somewhat perplexed by the following function:
// Open a file, then read from it. Close the file. return the data.
let getStringFromFile =
File.OpenRead("c:\\eo\\raw.txt")
|> fun s -> let r = new StreamReader(s)
let data = r.ReadToEnd
r.Close()
s.Close()
data
When I call this in FSI:
> let d = getStringFromFile();;
System.ObjectDisposedException: Cannot read from a closed TextReader.
at System.IO.__Error.ReaderClosed()
at System.IO.StreamReader.ReadToEnd()
at <StartupCode$FSI_0134>.$FSI_0134.main#()
Stopped due to error
This makes me think that getStringFromFile is being evaluated lazily--so I'm totally confused. I'm not getting something about how F# evaluates functions.
For a quick explanation of what's happening, lets start here:
let getStringFromFile =
File.OpenRead("c:\\eo\\raw.txt")
|> fun s -> let r = new StreamReader(s)
let data = r.ReadToEnd
r.Close()
s.Close()
data
You can re-write the first two lines of your function as:
let s = File.OpenRead(#"c:\eo\raw.txt")
Next, you've omitted the parentheses on this method:
let data = r.ReadToEnd
r.Close()
s.Close()
data
As a result, data has the type unit -> string. When you return this value from your function, the entire result is unit -> string. But look what happens in between assigning your variable and returning it: you closed you streams.
End result, when a user calls the function, the streams are already closed, resulting in the error you're seeing above.
And don't forget to dispose your objects by declaring use whatever = ... instead of let whatever = ....
With that in mind, here's a fix:
let getStringFromFile() =
use s = File.OpenRead(#"c:\eo\raw.txt")
use r = new StreamReader(s)
r.ReadToEnd()
You don't read from your file. You bind method ReadToEnd of your instance of StreamReader to the value data and then call it when you call getStringFromFile(). The problem is that the stream is closed at this moment.
I think you have missed the parentheses and here's the correct version:
// Open a file, then read from it. Close the file. return the data.
let getStringFromFile =
File.OpenRead("c:\\eo\\raw.txt")
|> fun s -> let r = new StreamReader(s)
let data = r.ReadToEnd()
r.Close()
s.Close()
data