Adobe AIR Native Extension TWAIN Image Scanner

2019-09-15 03:33发布

Currently I am working on Adobe AIR Native extension which gives possibilities to simple manipulate image scanner using TWAIN.
I use CTwain class found at http://www.codeproject.com/Articles/296/A-C-Wrapper-for-TWAIN

When i use this class in Windows Application (.exe) it works as expected, but in dll (which i need to create ane file) it crashes when Twain Device UI is closing (when scan complete or by clicking Cancel button)

I thing the problem is somewhere in DllMain.cpp file (possibly message loop), because in application with starting function APIENTRY _tWinMain it works perfect.

Code
DllMain.cpp

#include "stdafx.h"
#include "TwainCpp.h"
#include "resource.h"

using namespace std;

HWND g_hwnd = NULL;
HINSTANCE g_hInstance = NULL;
BOOL isValid = false;
BOOL isCreated = false;
CTwain *twain = NULL;

LRESULT CALLBACK WndProc(HWND hWnd, unsigned message, WPARAM wParam, LPARAM lParam)
{
    switch(message)
    {
        case WM_CREATE:         
            break;
        case WM_DESTROY:            
            PostQuitMessage(0);
            break;
    }   
    return DefWindowProc(hWnd, message, wParam, lParam);
}

BOOL CreateAppWindow()
{
    WNDCLASS  wc;
    wc.style = 0;
    wc.lpfnWndProc = WndProc;
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.hInstance = g_hInstance;
    wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = (HBRUSH)(COLOR_APPWORKSPACE + 1);
    wc.lpszMenuName =  "FRETwainMenu";
    wc.lpszClassName = "FRETwainClass";

    if(RegisterClass(&wc))
    {
        HWND hWnd;          
            char title[50];
            wsprintf(title, "FRETwain:%x", g_hInstance);
            hWnd = CreateWindow("FRETwainClass", title, WS_DISABLED,               CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,                      HWND_MESSAGE, NULL, g_hInstance, NULL);
            if(hWnd)
            {       
                g_hwnd = hWnd;                  
                return TRUE;                    
            }               
            return FALSE;       
    }   
    return FALSE;
}

DWORD WINAPI CreateAppThread()
{   
    if(CreateAppWindow())
    {
        MSG msg;
        isCreated = true;
        while(GetMessage(&msg, NULL, 0, 0) > 0)
        {
            if(twain != NULL){              
                twain->ProcessMessage(msg);             
            }   
            TranslateMessage(&msg);
            DispatchMessage(&msg);                      
        }       
        return TRUE;
    }
    return FALSE;
}

BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD reason, LPVOID lpvReserved)
{
    switch(reason)
    {
         case DLL_PROCESS_ATTACH:
            g_hInstance = hInstance;            
            HANDLE thread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)CreateAppThread, (LPVOID)NULL, 0, NULL);
            break;
    }        
    return TRUE;
}

FRETwain.cpp (context file)

#include <windows.h>
#include "FRETwain.h"

extern BOOL isValid;
extern BOOL isCreated;
extern HWND g_hwnd;
extern CTwain *twain;

extern "C"
{   
    FREObject AcquireTwain(FREContext ctx, void* funcData, uint32_t argc, FREObject argv[])
    {   
        FREObject result;
        uint32_t ret = 0;   
        if(isValid){
            twain->Acquire(TWCPP_ANYCOUNT);
            ret = 1;
        }   
        FRENewObjectFromBool(ret, &result);
       return result;
    }

    FREObject setDefaultDevice(FREContext ctx, void* funcData, uint32_t argc, FREObject argv[])
    {   
        FREObject result;
        uint32_t ret = 0;   
        if(isValid){
            twain->SelectSource();
            ret = 1;
        }
        FRENewObjectFromBool(ret, &result);
        return result;
    }

    FREObject initTwain(FREContext ctx, void* funcData, uint32_t argc, FREObject argv[])
    {       
        FREObject result;
        uint32_t isTwain = 0;   
        if(!isValid){
            twain = new CTwain(g_hwnd, ctx);
            isValid = twain->IsValidDriver();
        }       
        if(isValid)
            isTwain = 1;
        FRENewObjectFromBool(isTwain, &result);
        return result;
    }

}

2条回答
2楼-- · 2019-09-15 04:08

You don;t appear to have any thread synchronization.

First off, it looks like there is a race condition between the initialization of g_hwnd and its use in AcquireTwain etc. This probably isn't your problem but you should take that into account - if you access g_hwnd from the main AIR thread then you must synchronize access to it using a CRITICAL_SECTION or a mutex.

However, if you can maybe just use a critical section to ensure that g_hwnd is correctly initialized and then post messages to its message queue to asynchronously do the acquire. i.e. post a windows message to g_hwnd and then call CTwain::Acquire from within the thread.
You'd probably also need to create the CTwain from within the thread etc. Basically, just make it thread-safe.

I've never written a native extension for Windows (only for iOS) so unsure on this... but perhaps there is a way of getting a window handle from the AIR runtime that can be used without the need for a secondary thread (unless CTwain blocks?). This would make it a lot easier.

查看更多
Luminary・发光体
3楼-- · 2019-09-15 04:19

I found another Twain wraper - C++ version of EzTwain - works perfect
header file, source file

message loop called within same thread

void EZTAPI TWAIN_ModalEventLoop(void)  
{  
    MSG msg;  

    while ((nState >= 5) && !hDib && GetMessage((LPMSG)&msg, NULL, 0, 0)) {  
        if (!TWAIN_MessageHook ((LPMSG)&msg)) {  
            TranslateMessage ((LPMSG)&msg);  
            DispatchMessage ((LPMSG)&msg);  
        }  
    } // while  
} // TWAIN_ModalEventLoop 

Thanks anyway

查看更多
登录 后发表回答