dynarithmic / twain_library

Dynarithmic TWAIN Library, Version 5.x
Apache License 2.0
60 stars 25 forks source link

[Help]Concurrency from Java #19

Closed amaan75 closed 10 months ago

amaan75 commented 3 years ago

Hello Sir, i am trying to use the library from java with jni, i would like to know, if we can use this library to concurrently scan from multiple scanners at once, if so, how should i go about doing this. For more context, I plan to use this in a webservice context where every request is handled by a new thread do i have to do this in every thread? DTwainJavaAPI api = new DTwainJavaAPI(); api.DTWAIN_JavaSysInitialize();

or can i store this globally somewhere in a static variable and then keep using it again and again with different sources.

dynarithmic commented 3 years ago

Hi,

Two things:

1) Please read the help documentation concerning multiple threads and TWAIN. This is basically how TWAIN is set up -- you can have multiple threads, but there are restrictions. If a TWAIN session is started in thread x, you must access/acquire/change settings, etc. in thread x.

2) Regarding implementing this in Java itself, personally, I have not tried this, but there is a startTwainThread function that is utilized on start up.

You should go ahead and implement what you are trying to implement. If there are issues, and if I have time (which is kind of short), please create a small Java project (in Eclipse would be preferable), and I can take a look at it. The Java piece has not been updated in almost 2 years, so I can't vouch to how robust it is with respect to how it will react to using it concurrently.

dynarithmic commented 3 years ago

Upon looking at this a little bit more closely, you could try to create multiple DTwainJavaAPI instances, where each instance corresponds to a specific Java thread.

See this. It may describe a way to do this with Java.

amaan75 commented 3 years ago

hello @dynarithmic

package com.dtwain.test;

import com.dynarithmic.twain.*;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.UUID;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

import static com.dynarithmic.twain.DTwainJavaAPIConstants.*;

public class SimpleAcquire {
  public static void main(String[] args) {
    System.out.println("PROP" + System.getProperty("java.library.path"));
    System.out.println("ENV" + System.getenv("PATH"));
    SimpleAcquire s = new SimpleAcquire();
    try {
      s.SimpleTest();
    } catch (Exception e) {
      e.printStackTrace();
    }
  }

  public void SimpleTest() throws Exception {
    ExecutorService pool = Executors.newCachedThreadPool();
    Future<?> twain2_freeImage_software_scanner =
        pool.submit(runTask("TWAIN2 FreeImage Software Scanner"));
    Future<?> scannerTask = pool.submit(runTask("TWAIN2 FreeImage Software Scanner"));
    //    Future<?> submit = pool.submit(runTask("TW-Brother ADS-3600W"));
    //    submit.get();
    twain2_freeImage_software_scanner.get();
    scannerTask.get();
  }

  private Runnable runTask(String sourceName) {
    return () -> {
      try {
        scanFromSource(sourceName);
      } catch (Exception e) {
        e.printStackTrace();
      }
    };
  }

  private void scanFromSource(String sourceName) throws Exception {
    System.out.println("task for sourceName" + sourceName);
    DTwainJavaAPI api = new DTwainJavaAPI();
    api.DTWAIN_JavaSysInitialize();
    System.out.println("lib init complete" + sourceName+"Thread"+Thread.currentThread().getId());
    long twainSource = api.DTWAIN_SelectSourceByName(sourceName);
    DTwainSourceInfo dTwainSourceInfo = api.DTWAIN_GetSourceInfo(twainSource);
    for (int capNumber : api.DTWAIN_EnumSupportedCaps(twainSource)) {
      System.out.println(DTwainCapabilityHandler.toString(capNumber));
    }
    api.DTWAIN_SetCapValuesString(
        twainSource, DTWAIN_CV_ICAPXRESOLUTION, DTWAIN_CAPSET, new String[] {"100"});
    api.DTWAIN_SetCapValuesString(
        twainSource, DTWAIN_CV_ICAPYRESOLUTION, DTWAIN_CAPSET, new String[] {"100"});
    if (api.DTWAIN_IsDuplexSupported(twainSource)) {
      System.out.println("duplex supported for source" + sourceName);
      api.DTWAIN_EnableDuplex(twainSource, false);
    }
    System.out.println(new CDSI(dTwainSourceInfo));
    if (twainSource != 0) {
      DTwainAcquisitionArray acqArray =
          api.DTWAIN_AcquireNative(
              twainSource, DTwainJavaAPIConstants.DTWAIN_PT_RGB, 1, false, false);
      if (acqArray != null) {
        for (DTwainAcquisitionData dTwainAcquisitionData : acqArray.getAcquisitonArray()) {
          ByteArrayInputStream inputStream =
              new ByteArrayInputStream(dTwainAcquisitionData.getImageData(0));
          Path path = Paths.get("./" + UUID.randomUUID().toString() + ".jpg");
          System.out.println(path.toAbsolutePath());
          OutputStream outputStream = Files.newOutputStream(path, StandardOpenOption.CREATE);
          BufferedImage bufferedImage = ImageIO.read(inputStream);
          ImageIO.write(bufferedImage, "jpg", outputStream);
        }
      }
    }
    api.DTWAIN_JavaSysDestroy();
  }

  public void SIToString(DTwainSourceInfo si) {}

  public static class CDSI extends DTwainSourceInfo {

    CDSI(DTwainSourceInfo si) {
      super(
          si.getVersionInfo(),
          si.getManufacturer(),
          si.getProductFamily(),
          si.getProductName(),
          si.getMajorNum(),
          si.getMinorNum());
    }

    @Override
    public String toString() {
      return "CDSI{}" + getProductName();
    }
  }
}

I have modified the simple acquire form the Java Demos, to basically work with two devices, this is kind of like my use case, and if i get this portion to work i can most likely get my service to work as well. sometimes my code gets stuck at DTWAIN_SysInitialize and the other times i get this Process finished with exit code -1073740940 (0xC0000374) STATUS_HEAP_CORRUPTION from Windows.

dynarithmic commented 3 years ago

Thanks,.

I will take a look at it and let you know what I find.
.

amaan75 commented 3 years ago

FYI, something similar happens when i try the same thing on a .netcore 5.0 app https://github.com/amaan75/ScannerServiceDtwain This also i believe gets stuck on DTWAIN_SysInitialize. PS i am using windows 10 if that matters 64 bit, but running the app on 32 bit.

dynarithmic commented 3 years ago

I suggest you do the simplest of acquisitions first.

1) Show the TWAIN dialog and select the source manually. 2) Acquire to a file. 3) Check for error conditions for the DTWAIN functions you're calling.

Make sure that you can do both 1) and 2) first before trying anything beyond this. As to the error conditions, your code assumes that all of the functions return ok, when that may not be the case. For example, if DTWAIN_SysInitialize() returns 0, then there is no point trying to open a data source, acquire images, or call DTWAIN_EnumSources().


Given this, I tried your .NET sample. I am also using Windows 10, 64-bit (the application is 32-bit).

The program called DTWAIN_SysInitialize with no issues and was able to complete. It even got as far as selecting the data source.

However, there seems to be a strange issue with loading the sample TWAIN data source using your code. I have 7 data sources, where 1 of them is the TWAIN sample source. The TWAIN dialog shows 6 sources, but not the sample source. I don't know if this is an issue with .NET core 5.0, but for some reason, the TWAIN Data Source Manager rejects loading the sample TWAIN source under 5.0 core.

In a "normal" non .NET application such as the demo (DTWDEMO32U.exe), there is no issue at all with displaying all seven sources. As far as .NET, there is a sample C# demo, and I had no issues with this demo.

I have to debug your code, using TWAIN sample source, along with the open source TWAIN data source manager (TWAINDSM.DLL) to see what is happening. I attempted this, but the threading or whatever is happening is getting in the way of debugging effectively. For example, I would stop at a breakpoint in the native C++ code, but I would get spurious "COM interop error", basically destroying the debugging session while I'm inspecting or stepping over lines of the C++ code.

I will attempt this again, but won't be able to get to this immediately due to time constraints.


Also, you could also debug the code, as the entire source is available. You just need to turn on the native debugging option in your C# properties, and make sure your app uses dtwain32ud.dll instead of dtwain32u.dll. Also the symbols are in dtwain32ud.pdb. Finally, replace dtwain32u.cs in your project with dtwain32ud.cs. Once you do all that, you can basically debug DTWAIN and your application (provided you have the source, which is also found at the linked repo).

You could also debug the TWAIN DSM 2.0, but I wouldn't go that far right now.

dynarithmic commented 3 years ago

Also, if you don't have the DTWAIN resource text files available, DTWAIN_SysInitialize will not complete. You will get a dialog box stating that the DTWAIN resources could not be found. See the README page.

dynarithmic commented 10 months ago

Closing this issue.