1

I have a C# application that call a windows dll (fbwflib.dll). From this dll is called a function (FbwfIsFilterEnabled) that require two parameter passed by reference.

In C# my code is:

public class FBWF_Utilities
{
    [DllImport("fbwflib.dll", SetLastError = true)]
    static extern UIntPtr FbwfIsFilterEnabled(
       [MarshalAs(UnmanagedType.U1)]
       ref bool currentSession,
       [MarshalAs(UnmanagedType.U1)]
       ref bool nextSession
    );
    [DllImport("fbwflib.dll", SetLastError = true)]
    [return: MarshalAs(UnmanagedType.U4)]
    static extern uint FbwfEnableFilter();

    [DllImport("fbwflib.dll", SetLastError = true)]
    [return: MarshalAs(UnmanagedType.U4)]
    static extern uint FbwfDisableFilter();


    public static bool IsEnabledCurrent()
    {
        bool xCurrent = false;
        bool xNext = false;

        if (IsInstalled())
        {
            try
            {
                FbwfIsFilterEnabled(ref xCurrent, ref xNext);
            }
        catch (Exception ex)
            {
                MessageBox.Show("FBWF IsEnabledCurrent exception:\n\n" + ex.Message);
                return false;
            }
        }

        return xCurrent;
    }

Now I need to convert this code in Python code.

I have tried:

@staticmethod
def IsEnabled():
    dllObject = ctypes.cdll.LoadLibrary(r'C:\Windows\System32\fbwflib.dll')
    dllObject.FbwfIsFilterEnabled.argtypes = [ctypes.c_int, ctypes.c_int]
    dllObject.FbwfIsFilterEnabled.restype = None

    current = ctypes.c_int(0)
    next = ctypes.c_int(0)

    dllObject.FbwfIsFilterEnabled(ctypes.byref(current, 0), ctypes.byref(next, 0))
    messagebox.showinfo("", "Current: " + current + "\n" + "Next: " + next)

but I have an error: ctypes.ArgumentError: argument 1: :wrong type

I have tried other parametrizations but without any good result.

What is the correct way to load a library with parameters by reference?

Other function without parameter work well:

@staticmethod
def EnableFilter():
    dllObject = ctypes.cdll.LoadLibrary(r'C:\Windows\System32\fbwflib.dll')
    dllObject.FbwfEnableFilter()

@staticmethod
def DisableFilter():
    dllObject = ctypes.cdll.LoadLibrary(r'C:\Windows\System32\fbwflib.dll')
    dllObject.FbwfDisableFilter()

that work fine without any error.

A small description of the function that give me some trouble is here: https://www.pinvoke.net/default.aspx/fbwflib.FbwfIsFilterEnabled

EDIT:

changing with ctypes.pointer as suggested from Luke (thanks) now I had a different error:

Procedure called with not enough arguments (8 bytes missing) or wrong calling convention

referred to line:

dllObject.FbwfIsFilterEnabled(ctypes.byref(current, 0), ctypes.byref(next, 0))

changing the line as:

dllObject.FbwfIsFilterEnabled(ctypes.POINTER(current), ctypes.POINTER(next))

I receive the error: Type error: must be ctypes type

that let me know that byref calling was the right way probably.

changing considering bool type as done on my c# code:

@staticmethod
def IsEnabled():
    dllObject = ctypes.cdll.LoadLibrary(r'C:\Windows\System32\fbwflib.dll')
    dllObject.FbwfIsFilterEnabled.argtypes = [ctypes.POINTER(ctypes.c_bool), ctypes.POINTER(ctypes.c_bool)]
    dllObject.FbwfIsFilterEnabled.restype = None

    current = ctypes.c_bool()
    next = ctypes.c_bool()

    dllObject.FbwfIsFilterEnabled(ctypes.byref(current), ctypes.byref(next))
    messagebox.showinfo("", "Current: " + current + "\n" + "Next: " + next)

I receive the same error: Procedure called with not enough arguments (8 bytes missing) or wrong calling convention

I think that the solution is near but it seams that there is some detail missing

SOLUTION:

The solution finally come starting from

ctypes.windll.LoadLibrary

as suggested from Luke. I have removed argtypes and restype for now and after receiving c_long value that can be 0 or 1 a have obtained boolean condition.

The simple (I can say this now) code is:

@staticmethod
def IsEnabled():
    dllObject = ctypes.windll.LoadLibrary(r'C:\Windows\System32\fbwflib.dll')
    c = ctypes.c_int()
    # dllObject.FbwfIsFilterEnabled.argtypes = [ctypes.pointer(ctypes.c_int), ctypes.pointer(ctypes.c_int)]
    # dllObject.FbwfIsFilterEnabled.restype = ctypes.c_void_p

    current = ctypes.c_int()
    next = ctypes.c_int()

    dllObject.FbwfIsFilterEnabled(ctypes.byref(current), ctypes.byref(next))
    messagebox.showinfo("", "Current: " + str(bool(current)) + "\n" + "Next: " + str(bool(next)))

I hope that it can be usefull for others in order to not loose so many time like me around the solution that is near the initial code but that require hours to be fixed also because of the "nature" of this dll that required an embedded system and the installation of this particular library.

Many thanks

andy81dt
  • 11
  • 2

1 Answers1

0

Try replacing the line

    dllObject.FbwfIsFilterEnabled.argtypes = [ctypes.c_int, ctypes.c_int]

with

    dllObject.FbwfIsFilterEnabled.argtypes = [POINTER(ctypes.c_int), POINTER(ctypes.c_int)]

I also note that the function is defined to return a UIntPtr, so you might also want to try replacing the line

    dllObject.FbwfIsFilterEnabled.restype = None

with

    dllObject.FbwfIsFilterEnabled.restype = c_void_p

However, I don't have a copy of that DLL so I can't test that this change works.

Luke Woodward
  • 63,336
  • 16
  • 89
  • 104
  • Many thanks for the answer. I have edited my question with other test based to your suggestion. I can say that it is doesn't work but I think that the solution is closer than before. Yes this dll is not a common dll but is available normally on embedded system and specially on POS edition of windows. I am also interested to examples of other dll that has functions called by reference in order to test how to obtain a correct calling even on another dll. In this case do you know some functions on a windows dll that can be used as preliminary test? Many thanks – andy81dt Sep 20 '18 at 15:55
  • The function you are calling returns something (a `UIntPtr` according to the link). However you have set its `restype` to `None`. Does it help if you change the return type as per my edited question? – Luke Woodward Sep 20 '18 at 16:51
  • The error is the same: Procedure called with not enough arguments (8 bytes missing) or wrong calling convention, even with return type ctypes.POINTER(ctypes.c_int) the error is the same. With ctypes.POINTER(c) where c is declared above as: c = ctypes.c_int() the error is Type error: must be a ctypes type because is a pointer to data and not pointer of type of data. – andy81dt Sep 21 '18 at 11:24
  • The only other suggestion I have is to try replacing `ctypes.cdll.LoadLibrary` with `ctypes.windll.LoadLibrary`. [This answer](https://stackoverflow.com/a/15664100) suggests that `stdcall` is the default calling convention with P/Invoke code, and to use `stdcall` with `ctypes` you have to use `ctypes.windll` rather than `ctypes.cdll` (https://docs.python.org/3.6/library/ctypes.html#loading-dynamic-link-libraries). – Luke Woodward Sep 21 '18 at 21:13
  • Interesting. Thanks again, I will try. – andy81dt Sep 21 '18 at 22:07