Define attributes in method parameters in F# - f#

I'm doing a DLL import in F# and then passing some parameters for a method.
[<DllImport("winspool.Drv", EntryPoint = "OpenPrinterA", SetLastError = true, CharSet = CharSet.Ansi, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)>]
extern bool OpenPrinter([<MarshalAs(UnmanagedType.LPStr)>] szPrinter)
A parameter with attributes must also receive a name is the compilers answer.
This method must have a MarshalAs attribute before it, like we have in C#
[DllImport("winspool.Drv", EntryPoint = "OpenPrinterA", SetLastError = true, CharSet = CharSet.Ansi, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
private static extern bool OpenPrinter([MarshalAs(UnmanagedType.LPStr)] string szPrinter);
The parameter of OpenPrinter is a string called szPrinter, but it have an attribute [MarshalAs(UnmanagedType.LPStr])
F# doesn't accept this like C#.
How to define attributes inside method parameters?

It's because your extern function is expecting a type for the parameter:
extern bool OpenPrinter([<MarshalAs(UnmanagedType.LPStr)>] string szPrinter)

Related

DllImport in FSI

Need to port following C# code to F#:
[DllImport("libc", SetLastError = true)]
private static extern int chmod(string pathname, int mode);
What is wrong in following code? Tried it in FSI on Mac and getting error FS0193: internal error: Method 'FSI_0020+Libc.chmod' does not have a method body.
module Libc =
open System.Runtime.InteropServices
[<DllImport("libc", SetLastError = true)>]
extern int chmod(string pathname, int mode)
[<DllImport ("libc", EntryPoint = "chmod", SetLastError = true)>]
extern int sys_chmod (string _path, uint32 _mode)
let chmodCarry mode path = sys_chmod(path, mode)
And what to do with private and static in this case? I would make sys_chmod private and expose the carried chmod function only.
Or is there any other more portable way to make a file executable?

Member with no value in F#

I'm trying to convert the following C# code to F#:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
private class DOCINFOA
{
[MarshalAs(UnmanagedType.LPStr)]
public string nomeDocumento;
[MarshalAs(UnmanagedType.LPStr)]
public string arquivoSaida;
[MarshalAs(UnmanagedType.LPStr)]
public string tipoDado;
}
I'm doing the following:
namespace Printer
module RawPrinterHelper =
[<StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)>]
type DOCINFOA =
[<MarshalAs(UnmanagedType.LPStr)>]
member this.nomeDocumento
[<MarshalAs(UnmanagedType.LPStr)>]
member this.arquivoSaida
[<MarshalAs(UnmanagedType.LPStr)>]
member this.tipoDado
But I receive:
A type definition requires one or more members. Can I let these members empty?
You can use val instead of member:
[<StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)>]
type DOCINFOA =
[<MarshalAs(UnmanagedType.LPStr)>]
val nomeDocumento : string
[<MarshalAs(UnmanagedType.LPStr)>]
val arquivoSaida : string
[<MarshalAs(UnmanagedType.LPStr)>]
val tipoDado : string

Can the free_function be a static class method?

This is a follow-up question to How to write void pointer typedefs in vapi files?
I now have four almost identical [Compact] classes that represent handles allocated with unixODBCs SQLAllocHandle function.
The first one (for the ENV type handle) looks like this:
[CCode (cname = "void", free_function = "EnvironmentHandle.free")]
[Compact]
public class EnvironmentHandle {
[CCode (cname = "SQLAllocHandle")]
private static Return allocate_internal (HandleType type, void* nothing, out EnvironmentHandle output_handle);
public static Return allocate (out EnvironmentHandle output_handle) {
return allocate_internal (HandleType.ENV, null, out output_handle);
}
[CCode (cname = "SQLFreeHandle")]
private static Return free_internal (HandleType type, EnvironmentHandle handle);
public static Return free (EnvironmentHandle handle) {
return free_internal (HandleType.ENV, handle);
}
}
This doesn't compile.
Is it possible to use a static class method as the free_function?
If not, is there at least a way to write a custom free_function in the vapi file?
I need a custom function because the SQLFreeHandle function takes the handle type and the handle as an argument.
From the vapi users perspective all that is really important is:
[CCode (cname = "void")]
[Compact]
public class EnvironmentHandle {
public static Return allocate (out EnvironmentHandle output_handle);
}
The only other solution would be to use a [SimpleType] struct as suggested by apmasell in the original question. That would hide the fact that a SQLHANLDE is really a reference type.
The full code of my current implementation is available online:
https://github.com/antiochus/unixodbc-vala/tree/0486f54dc3f86d9c8bf31071980e4f171aca9591
No. The free_function is a C function, not a Vala function and it cannot take any context. You have two options:
Write a C macro in an extra header file to do what you want and bind the macro as the free function.
Bind the free function as a static method that takes an owned instance of the object:
[CCode (cname = "SQLFreeHandle")]
public static Return free(HandleType type, owned EnvironmentHandle handle);
EnvrionmentHandle foo = ...;
EnvironmentHandle.free(HandleType.ENV, (owned) foo);

JNIEnv call from within monodroid application

This is the Java class i'm accessing through JNIEnv in my monodroid application
package mypackage;
import android.util.Log;
public class JavaScriptInterface {
public String submitAns = "";
// The JNI in the original question uses a default constructor.
// Either provide one explicitly or use the implicit one...
public JavaScriptInterface()
{
}
public String getSelctd()
{
return submitAns;
}
}
I'm able to instantiate the class by the following statements:
Java.Lang.Object jclassWrp_;
IntPtr JavaScriptInterface_Class = JNIEnv.FindClass("mypackage.JavaScriptInterface");
IntPtr JavaScriptInterface_ctor = JNIEnv.GetMethodID(JavaScriptInterface_Class, "<init>", "()V"); //(Landroid/context/Context;)V
IntPtr jsInterfaceinstance_ = JNIEnv.NewObject(JavaScriptInterface_Class, JavaScriptInterface_ctor);
jclassWrp_ = new Java.Lang.Object(jsInterfaceinstance_, JniHandleOwnership.TransferGlobalRef);
But when i try to create the object to access the getSelctd() method:
IntPtr ipApid = JNIEnv.GetMethodID(jclassWrp_, "getSelctd", "()Ljava/lang/String;");
It throws NoSuchMethodExist Exception...
Please tell me whether i'm doing it the right way and what i'm missing here...
I'm able to instantiate the class by the following statements:
Java.Lang.Object jclassWrp_;
IntPtr JavaScriptInterface_Class = JNIEnv.FindClass("mypackage.JavaScriptInterface");
JNI use should use JNI conventions, thus mypackage/JavaScriptInterface (note / instead of .).
IntPtr JavaScriptInterface_ctor = JNIEnv.GetMethodID(JavaScriptInterface_Class, "<init>", "()V");
IntPtr jsInterfaceinstance_ = JNIEnv.NewObject(JavaScriptInterface_Class, JavaScriptInterface_ctor);
jclassWrp_ = new Java.Lang.Object(jsInterfaceinstance_, JniHandleOwnership.TransferGlobalRef);
JNIEnv.NewObject() returns a local ref, not a global ref, so you want JniHandleOwnership.TransferLocalRef.
But when i try to create the object to access the getSelctd() method:
IntPtr ipApid = JNIEnv.GetMethodID(jclassWrp_, "getSelctd", "()Ljava/lang/String;");
JNIEnv.GetMethodID() takes a class handle, not an instance. Firstly, the above shouldn't compile (Java.Lang.Object != IntPtr). Secondly, jclassWrp contains a mypackage.JavaScriptInterface instance, not the mypackage.JavaScriptInterface Class instance.
Instead, do:
IntPtr ipApid = JNIEnv.GetMethodID(JavaScriptInterface_Class, "getSelctd", "()Ljava/lang/String;");
Finally, don't forget to JNIEnv.DeleteGlobalRef(JavaScriptInterface_Class) when you don't need it anymore, otherwise you'll leak the gref.
Complete code:
// FindClass() returns a gref; must be freed (see below)
IntPtr JavaScriptInterface_Class = JNIEnv.FindClass("mypackage/JavaScriptInterface");
// MethodIDs do not need to be freed
IntPtr JavaScriptInterface_ctor = JNIEnv.GetMethodID(JavaScriptInterface_Class,
"<init>", "()V");
IntPtr JavaScriptInterface_getSelctd = JNIEnv.GetMethodID(JavaScriptInterface_Class,
"getSelctd", "()Ljava/lang/String;");
// JNIEnv.NewObject() & JNIEnv.CallObjectMethod() return lrefs; freed below
IntPtr lrefInstance = JNIEnv.NewObject(JavaScriptInterface_Class,
JavaScriptInterface_ctor);
IntPtr lrefSelectd = JNIEnv.CallObjectMethod(jsInterfaceinstance_, ipApid);
// JniHandleOwnership.TransferLocalRef causes lrefSelectd to be released for us
string selected = JNIEnv.GetString(lrefSelectd, JniHandleOwnership.TransferLocalRef);
// Resource cleanup
JNIEnv.DeleteLocalRef(lrefInstance);
JNIEnv.DeleteGlobalRef(JavaScriptInterface_Class);

Virtual printer port monitor installing

I have a port monitor dll, that I instaling by call AddMonitor function of the spooler. But when I want uninstal this monitor, the DeleteMonitor function return errorcode 3008 - "The specified print monitor is currently in use". How I can free my monitor dll?
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
private class MONITOR_INFO_2
{
[MarshalAs(UnmanagedType.LPStr)]
public string pName;
[MarshalAs(UnmanagedType.LPStr)]
public string pEnvironment;
[MarshalAs(UnmanagedType.LPStr)]
public string pDLLName;
}
[DllImport("winspool.Drv", EntryPoint = "AddMonitorA", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
private static extern bool AddMonitor(
[MarshalAs(UnmanagedType.LPStr)] string Name,
Int32 Level,
[In, MarshalAs(UnmanagedType.LPStruct)] MONITOR_INFO_2 mi2);
[DllImport("winspool.Drv", EntryPoint = "DeleteMonitorA", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
private static extern bool DeleteMonitor(
[MarshalAs(UnmanagedType.LPStr)] string pNullServerName,
[MarshalAs(UnmanagedType.LPStr)] string pNullEnvironment,
[MarshalAs(UnmanagedType.LPStr)] string MonitorName);
private unsafe void InstallMonitor(string monitorName, string dllName)
{
MONITOR_INFO_2 mi2 = new MONITOR_INFO_2();
mi2.pName = monitorName;
mi2.pEnvironment = null;
mi2.pDLLName = dllName;
try
{
bool bRet = AddMonitor(null, 2, mi2);
if (!bRet)
throw new Win32Exception(Marshal.GetLastWin32Error());
}
catch (Exception e)
{
if (!DeleteMonitor(null, null, monitorName))
{
throw new Win32Exception(Marshal.GetLastWin32Error());
}
bRet = AddMonitor(null, 2, mi2);
if (!bRet)
throw new Win32Exception(Marshal.GetLastWin32Error());
}
}
You will not be able to Delete a Port Monitor via the DeleteMonitor call if there is one or more printer objects currently using a port of that type.
This leaves you with several options:
Swap the port of all affected printers to another port. (Best to use something like LPT1: since its always there).
Delete all printers using the port.
Stop the spooler service and remove the appropriate entries from registry (HKLM\SYSTEM\CurrentControlSet\Control\Print\Monitors) then restart the spooler. This will leave the affected printers there but they will be unusable.

Resources