takuya-takeuchi / WinBiometricDotNet

Windows Bitometric Framework .NET wrapper
MIT License
32 stars 12 forks source link

AsyncOpen method can not use callback #6

Open takuya-takeuchi opened 6 years ago

takuya-takeuchi commented 6 years ago

Just after call AsyncOpenFramework method with WINBIO_ASYNC_NOTIFY_CALLBACK, library throw exception with E_OUTOFMEMORY

takuya-takeuchi commented 6 years ago

After turned on Enable native code debugging, IDE show the following dialog

image

takuya-takeuchi commented 6 years ago

In unmanaged application, despite Win32 sample is x86 or x64, it works fine.

image

takuya-takeuchi commented 6 years ago

C++/CLI console apps also works fine.

#include "stdafx.h"

using namespace System;

void CALLBACK WINBIO_ASYNC_COMPLETION_CALLBACK(PWINBIO_ASYNC_RESULT AsyncResult)
{
}

int main(array<System::String ^> ^args)
{
    WINBIO_ASYNC_NOTIFICATION_METHOD NotificationMethod = WINBIO_ASYNC_NOTIFY_CALLBACK;
    HWND  TargetWindow = NULL;
    UINT  MessageCode = 0;
    PVOID UserData = NULL;
    BOOL  AsynchronousOpen = TRUE;
    WINBIO_FRAMEWORK_HANDLE FrameworkHandle;
    HRESULT hr = WinBioAsyncOpenFramework(
        NotificationMethod,
        TargetWindow,
        MessageCode,
        WINBIO_ASYNC_COMPLETION_CALLBACK,
        UserData,
        AsynchronousOpen,
        &FrameworkHandle
    );

    return 0;
}
takuya-takeuchi commented 6 years ago

Create C++/CLI library which call AsyncOpenFramework and C$ console app which call C++/CLI library, then it works fine.

#include "stdafx.h"

#include "ClassLibrary1.h"

namespace ClassLibrary1 {

    void CALLBACK WINBIO_ASYNC_COMPLETION_CALLBACK(PWINBIO_ASYNC_RESULT AsyncResult)
    {
    }

    HRESULT Class1::Test()
    {
        WINBIO_ASYNC_NOTIFICATION_METHOD NotificationMethod = WINBIO_ASYNC_NOTIFY_CALLBACK;
        HWND  TargetWindow = NULL;
        UINT  MessageCode = 0;
        PVOID UserData = NULL;
        BOOL  AsynchronousOpen = TRUE;
        WINBIO_FRAMEWORK_HANDLE FrameworkHandle;
        HRESULT hr = WinBioAsyncOpenFramework(
            NotificationMethod,
            TargetWindow,
            MessageCode,
            WINBIO_ASYNC_COMPLETION_CALLBACK,
            UserData,
            AsynchronousOpen,
            &FrameworkHandle
        );

        return hr;
    }
}
using System;

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            var test = new ClassLibrary1.Class1();
            var ret = test.Test();
            Console.WriteLine(ret);
        }
    }
}
takuya-takeuchi commented 6 years ago

Minimum C# apps does not work.

using System;
using System.Runtime.InteropServices;

using BOOL = System.Int32;
using HRESULT = System.Int32;
using HWND = System.IntPtr;
using PVOID = System.IntPtr;
using UINT = System.UInt32;

using WINBIO_FRAMEWORK_HANDLE = System.UInt32;

namespace ConsoleApp2
{
    class Program
    {

        private const string DllName = "Winbio.dll";

        public enum WINBIO_ASYNC_NOTIFICATION_METHOD
        {

            WINBIO_ASYNC_NOTIFY_NONE = 0,

            WINBIO_ASYNC_NOTIFY_CALLBACK,

            WINBIO_ASYNC_NOTIFY_MESSAGE,

            WINBIO_ASYNC_NOTIFY_MAXIMUM_VALUE

        }

        public delegate void WINBIO_ASYNC_COMPLETION_CALLBACK([In] IntPtr AsyncResult);

        [DllImport(DllName)]
        public static extern HRESULT WinBioAsyncOpenFramework([In]  WINBIO_ASYNC_NOTIFICATION_METHOD NotificationMethod,
                                                              [In]  HWND TargetWindow,
                                                              [In]  UINT MessageCode,
                                                              [In]  WINBIO_ASYNC_COMPLETION_CALLBACK CallbackRoutine,
                                                              [In]  PVOID UserData,
                                                              [In]  BOOL AsynchronousOpen,
                                                              [Out] out WINBIO_FRAMEWORK_HANDLE FrameworkHandle);

        private static void AsyncCompletedCallback(IntPtr asyncResult)
        {
        }

        static void Main(string[] args)
        {
            var NotificationMethod = WINBIO_ASYNC_NOTIFICATION_METHOD.WINBIO_ASYNC_NOTIFY_CALLBACK;
            IntPtr TargetWindow = IntPtr.Zero;
            UINT MessageCode = 0;
            PVOID UserData = IntPtr.Zero;
            var AsynchronousOpen = 1;
            var hr = WinBioAsyncOpenFramework(NotificationMethod,
                                              TargetWindow,
                                              0,
                                              AsyncCompletedCallback,
                                              UserData,
                                              AsynchronousOpen,
                                              out var h);
            Console.WriteLine(hr);
        }
    }
}
takuya-takeuchi commented 6 years ago

Win32 with dynamic call looks like p/invoke by using LoadLibrary. It does not link any Windows Biometric Framework library. It works fine.

#include "stdafx.h"

#include "windows.h"
#include "WinBio.h"

typedef HRESULT(WINAPI *WINBIOASYNCOPENFRAMEWORK)
(
    _In_ WINBIO_ASYNC_NOTIFICATION_METHOD,
    _In_opt_ HWND,
    _In_opt_ UINT,
    _In_opt_ PWINBIO_ASYNC_COMPLETION_CALLBACK,
    _In_opt_ PVOID,
    _In_ BOOL,
    _Out_opt_ WINBIO_FRAMEWORK_HANDLE *
);

void CALLBACK WINBIO_ASYNC_COMPLETION_CALLBACK(PWINBIO_ASYNC_RESULT AsyncResult)
{
}

int main()
{
    HMODULE hModule = LoadLibrary(_T("WinBio.dll"));
    WINBIOASYNCOPENFRAMEWORK pFunc = (WINBIOASYNCOPENFRAMEWORK)GetProcAddress(hModule, "WinBioAsyncOpenFramework");

    WINBIO_ASYNC_NOTIFICATION_METHOD NotificationMethod = WINBIO_ASYNC_NOTIFY_CALLBACK;
    HWND  TargetWindow = NULL;
    UINT  MessageCode = 0;
    PVOID UserData = NULL;
    BOOL  AsynchronousOpen = TRUE;
    WINBIO_FRAMEWORK_HANDLE FrameworkHandle;
    HRESULT hr = pFunc(
        NotificationMethod,
        TargetWindow,
        MessageCode,
        WINBIO_ASYNC_COMPLETION_CALLBACK,
        UserData,
        AsynchronousOpen,
        &FrameworkHandle
    );

    return 0;
}
takuya-takeuchi commented 6 years ago

C# + LoadLibrary. It does not work.

using System;
using System.Runtime.InteropServices;

using BOOL = System.Int32;
using HRESULT = System.Int32;
using HWND = System.IntPtr;
using PVOID = System.IntPtr;
using UINT = System.UInt32;

using WINBIO_FRAMEWORK_HANDLE = System.UInt32;

namespace ConsoleApp4
{
    class Program
    {

        private const string DllName = "Winbio.dll";

        public enum WINBIO_ASYNC_NOTIFICATION_METHOD
        {

            WINBIO_ASYNC_NOTIFY_NONE = 0,

            WINBIO_ASYNC_NOTIFY_CALLBACK,

            WINBIO_ASYNC_NOTIFY_MESSAGE,

            WINBIO_ASYNC_NOTIFY_MAXIMUM_VALUE

        }

        [DllImport("kernel32", CharSet = CharSet.Unicode, SetLastError = true)]
        internal static extern IntPtr LoadLibrary(string lpFileName);

        [DllImport("kernel32", SetLastError = true)]
        internal static extern bool FreeLibrary(IntPtr hModule);

        [DllImport("kernel32", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = false)]
        internal static extern IntPtr GetProcAddress(IntPtr hModule, string lpProcName);

        public delegate void WINBIO_ASYNC_COMPLETION_CALLBACK([In] IntPtr AsyncResult);

        public delegate HRESULT WinBioAsyncOpenFramework([In]  WINBIO_ASYNC_NOTIFICATION_METHOD NotificationMethod,
                                                         [In]  HWND TargetWindow,
                                                         [In]  UINT MessageCode,
                                                         [In]  WINBIO_ASYNC_COMPLETION_CALLBACK CallbackRoutine,
                                                         [In]  PVOID UserData,
                                                         [In]  BOOL AsynchronousOpen,
                                                         [Out] out WINBIO_FRAMEWORK_HANDLE FrameworkHandle);

        private static void AsyncCompletedCallback(IntPtr asyncResult)
        {
        }

        static void Main(string[] args)
        {
            IntPtr handle = LoadLibrary(DllName);
            IntPtr funcPtr = GetProcAddress(handle, nameof(WinBioAsyncOpenFramework));

            WinBioAsyncOpenFramework func = (WinBioAsyncOpenFramework)Marshal.GetDelegateForFunctionPointer(funcPtr, typeof(WinBioAsyncOpenFramework));

            var NotificationMethod = WINBIO_ASYNC_NOTIFICATION_METHOD.WINBIO_ASYNC_NOTIFY_CALLBACK;
            IntPtr TargetWindow = IntPtr.Zero;
            UINT MessageCode = 0;
            PVOID UserData = IntPtr.Zero;
            var AsynchronousOpen = 1;
            var hr = func(NotificationMethod,
                          TargetWindow,
                          0,
                          AsyncCompletedCallback,
                          UserData,
                          AsynchronousOpen,
                          out var h);

            Console.WriteLine(hr);
        }
    }
}
takuya-takeuchi commented 6 years ago

C++/CLI and C#, but pass the delegate pointer defined in managed to C++/CLI side. It does not work

#include "stdafx.h"

#include "ClassLibrary1.h"

namespace ClassLibrary1 {

    void CALLBACK WINBIO_ASYNC_COMPLETION_CALLBACK(PWINBIO_ASYNC_RESULT AsyncResult)
    {
    }

    HRESULT Class1::Test()
    {
        WINBIO_ASYNC_NOTIFICATION_METHOD NotificationMethod = WINBIO_ASYNC_NOTIFY_CALLBACK;
        HWND  TargetWindow = NULL;
        UINT  MessageCode = 0;
        PVOID UserData = NULL;
        BOOL  AsynchronousOpen = TRUE;
        WINBIO_FRAMEWORK_HANDLE FrameworkHandle;
        HRESULT hr = WinBioAsyncOpenFramework(
            NotificationMethod,
            TargetWindow,
            MessageCode,
            WINBIO_ASYNC_COMPLETION_CALLBACK,
            UserData,
            AsynchronousOpen,
            &FrameworkHandle
        );

        return hr;
    }
    HRESULT Class1::Test2(void* func)
    {
        WINBIO_ASYNC_NOTIFICATION_METHOD NotificationMethod = WINBIO_ASYNC_NOTIFY_CALLBACK;
        HWND  TargetWindow = NULL;
        UINT  MessageCode = 0;
        PVOID UserData = NULL;
        BOOL  AsynchronousOpen = TRUE;
        WINBIO_FRAMEWORK_HANDLE FrameworkHandle;
        HRESULT hr = WinBioAsyncOpenFramework(
            NotificationMethod,
            TargetWindow,
            MessageCode,
            (PWINBIO_ASYNC_COMPLETION_CALLBACK)func,
            UserData,
            AsynchronousOpen,
            &FrameworkHandle
        );

        return hr;
    }
}
using System;
using System.Runtime.InteropServices;

namespace ConsoleApp1
{
    class Program
    {
        public delegate void WINBIO_ASYNC_COMPLETION_CALLBACK([In] IntPtr AsyncResult);

        private static void AsyncCompletedCallback(IntPtr asyncResult)
        {
        }

        static unsafe void Main(string[] args)
        {
            var test = new ClassLibrary1.Class1();
            var ret = test.Test();
            Console.WriteLine(ret);

            var ptr = Marshal.GetFunctionPointerForDelegate<WINBIO_ASYNC_COMPLETION_CALLBACK>(AsyncCompletedCallback);
            ret = test.Test2((void*)ptr);
            Console.WriteLine(ret);
        }
    }
}
takuya-takeuchi commented 6 years ago
App Constitution Result
Win32 OK
Win32 (by LoadLibrary) OK
C++/CLI app OK
C++/CLI (defined here) +C# app (.NET Framework) OK
C++/CLI (defined here) +C# app (.NET Framework, pass the delegate pointer) NG
C# app (.NET Framework) NG
C# app (by LoadLibrary) (.NET Framework) NG
C# app (.NET Core) NG

So