Jump to content
Dave Nottage

DelphiTwain on 64-bit

Recommended Posts

Has anyone managed to make DelphiTwain:

 

  http://www.kluug.net/delphitwain.php

 

work on 64-bit? The code loads the Twain DLL OK (TWAINDSM.DLL), however for me it fails in the LoadSourceManager routine:

 

FTwainProc(Info, nil, DG_CONTROL, DAT_PARENT, MSG_OPENDSM, @FVirtualWindow)

 

Which returns a result of TWRC_FAILURE. The corresponding condition code is TWCC_BADVALUE

Share this post


Link to post

You followed the instructions for 64 bit compilers, made the necessary change to the code outlined on the page you linked, obtained 64 bit drivers etc?

Share this post


Link to post
16 hours ago, David Heffernan said:

You followed the instructions for 64 bit compilers, made the necessary change to the code outlined on the page you linked, obtained 64 bit drivers etc?

Yes, since as I said, it loads the driver OK. The problem comes after that

Share this post


Link to post
Posted (edited)

Even so, is TWINDSM.DLL you're using also 64-bits? (Is this the place to get current ones?)

Edited by stijnsanders

Share this post


Link to post
On 6/15/2019 at 6:38 AM, stijnsanders said:

Even so, is TWINDSM.DLL you're using also 64-bits?

Yes, TWAINDSM.DLL is 64 bits.

 

On 6/15/2019 at 6:38 AM, stijnsanders said:

(Is this the place to get current ones?)

From here:  https://github.com/twain/twain-dsm/tree/master/Releases/dsm_020402/windows/64

 

Though I've now tried those from the link you gave, with the same result.

 

In case anyone asks: I have already also verified that the correct DLL is being loaded by using GetModuleName for the handle returned.

 

 

Share this post


Link to post
On 6/13/2019 at 9:03 AM, Dave Nottage said:

Which returns a result of TWRC_FAILURE. The corresponding condition code is TWCC_BADVALUE

So you provided a "bad value" :classic_blink:

 

Here it works fine with VueScan as 64 bit scan-sourcedriver.

 

What/which scannerdriver did you try/use?

 

Share this post


Link to post
10 hours ago, rvk said:

What/which scannerdriver did you try/use?

It works for Win32, and reports the following sources:

 

Brother DS-620

WIA-Apple iPhone (my iPhone)

WIA-Galaxy Tab Active2 (an Android device connected to the computer)

 

For Win64, it errors, as per my original post

 

Share this post


Link to post
Posted (edited)

On 64 bit it worked for me but I did't have any scan-sources.

WIA sources are 32-bit and not visible in 64 bit for me.

Installing the VueScan gave me a 64 bit source.

 

What Delphi version do you use?

Are you using VLC of FMX?

Did you try the DelphiTwain\examples\VCL2\DelphiTwainDemo2 example? (which worked for me on 64 bit)

 

Edited by rvk

Share this post


Link to post

Delphi Rio 10.3.1, VCL.

 

I was having problems with refactored code based on the original. I went back to the very original code and managed to make it work, so something must have gone awry in the refactor.

 

Next time it is refactored there'll be incremental testing 🙂

 

Next issue is: when it is used in a DLL, the app locks up after the scan has completed.

Share this post


Link to post
4 hours ago, Dave Nottage said:

Next issue is: when it is used in a DLL, the app locks up after the scan has completed.

Hard to say what could be the cause without example-source of DLL and calling procedure.

 

Share this post


Link to post

I discovered that it is throwing an AV at this part of TTwainSource.ReadNative:

  DibInfo := GlobalLock(Handle);
  ColorTableSize := (DibNumColors(DibInfo) * SizeOf(RGBQUAD)); // <---- AV here when it calls DibNumColors, as DibInfo is nil

The code is inside a DLL written for Windows Terminal Services. The same code works fine in a DLL that is just loaded via a plain application, so perhaps it has something to with WTS?

 

If I call SysErrorMessage(GetLastError) after the GlobalLock, the message is:

 

  The handle is invalid.

 

The value for Handle is non-zero.

 

Share this post


Link to post
Posted (edited)

Well, apparently GlobalLock failed with an invalid handle. Why is that? You need to work out where Handle came from and why it is not valid. 

Edited by David Heffernan

Share this post


Link to post
31 minutes ago, David Heffernan said:

You need to work out where Handle came from

It came from this call in the TTwainSource.TransferImages method:

rc := Owner.TwainProc(AppInfo, @Structure, DG_IMAGE, DAT_IMAGENATIVEXFER, MSG_GET, @hNative)

(TwainProc is the method exported by the Twain DLL)

32 minutes ago, David Heffernan said:

why it is not valid.

The more pertinent question is: why does it return a valid handle in a DLL that is not loaded by WTS, and why is it apparently invalid in a DLL loaded by WTS? I figured there might be something else I need to do to ensure that it returns a valid handle in WTS.

 

 

Share this post


Link to post

Are you using a redirected scanner in WTS? Or is the scanner-software (and 64bit driver) directly installed on the WTS machine?

 

Does the DelphiTwainDemo2 example work in WTS?

 

1 hour ago, Dave Nottage said:

The code is inside a DLL written for Windows Terminal Services. The same code works fine in a DLL that is just loaded via a plain application, so perhaps it has something to with WTS?

Not sure what you mean by this? In Windows Terminal Services (now called Remote Desktop Services, RDS) you also just run plain applications, don't you?

Or do you mean it's running under Windows Services? (which is hugely different)

 

Share this post


Link to post
52 minutes ago, Dave Nottage said:

The more pertinent question is: why does it return a valid handle in a DLL that is not loaded by WTS, and why is it apparently invalid in a DLL loaded by WTS?

Given that you are the only one with any code, and any ability to debug, you are best placed to investigate.

 

My advice is to stop guessing and do some old fashioned debugging.

Share this post


Link to post
4 hours ago, rvk said:

is the scanner-software (and 64bit driver) directly installed on the WTS machine?

That is correct. 

 

4 hours ago, rvk said:

Not sure what you mean by this? In Windows Terminal Services (now called Remote Desktop Services, RDS) you also just run plain applications, don't you?

This is what I mean:

 

https://docs.microsoft.com/en-us/windows/win32/termserv/virtual-channel-client-dll

 

So yes, RDS; I'm just used to the other terminology.

As above, the DLL is on the same machine as the Twain driver. RDS loads the DLL when a session is started to the remote application, which communicates to the virtual channel client DLL via RDS. The remote application notifies the virtual channel to scan an image, and the DLL makes calls via the Delphi Twain code to enumerate the sources, and start scanning an image. The scanning "in progress" dialog shows OK, however the code fails in TTwainSource.ReadNative, as described earlier.

The identical code to allow the user to select the source and start the scan works in a DLL loaded by a test app; it fails in the virtual channel client (RDS) DLL.

 

I just hoped someone might know why there's a difference, because debugging isn't telling me anything other than Windows claiming that the handle is invalid.

Share this post


Link to post

Nobody other than you knows where this handle comes from. For all we know you've got a simple 32/64 bit truncation error. But we can't see the relevant code. 

Share this post


Link to post
Posted (edited)
27 minutes ago, Dave Nottage said:

As above, the DLL is on the same machine as the Twain driver. RDS loads the DLL when a session is started to the remote application, which communicates to the virtual channel client DLL via RDS.

So, the drivers are not loaded on the server itself?

 

I'm not familiar with Virtual Channels in RDP, but I think this would mean you have the DLL on the local client and want the application, which runs on the server side in RDP, have access to the local scanner through the Virtual Channels-technology? Just like a Redirected Printer uses Virtual Channels.

 

That's a whole lot of places where things can go wrong (that's why I asked if the scanner and drivers and DLL where locally on the server side in the RDP session itself).

 

What was the value of the return-code on this line (in TTwainSource.TransferImages for TransferMode ttmNative):

rc := Owner.TwainProc(AppInfo, @Structure, DG_IMAGE, DAT_IMAGENATIVEXFER, MSG_GET, @hNative);

That seems to be where hNative gets its value (which fails).

It seems the value is not checked for success there ( if rc <> TWRC_SUCCESS then ... should give error )

Edited by rvk

Share this post


Link to post
15 minutes ago, rvk said:

So, the drivers are not loaded on the server itself?

Not on the remote machine no, since it does not have the scanner connected; the local machine does, where the RDS DLL resides.

 

16 minutes ago, rvk said:

It seems the value is not checked for success there ( if rc <> TWRC_SUCCESS then ... should give error )

In this call, rc can be one of TWRC_XFERDONE, TWRC_CANCEL, or one of the error values. In this case, it is returning TWRC_XFERDONE (which is why it proceeds to the ReadNative call, where the error occurs, as per one of my earlier messages) so one would hope that it is returning a valid value for hNative. As far as GlobalLock is concerned, it apparently is not.

Share this post


Link to post

Are your connecting client, the used mstsc.exe and RDP-session (including your app) ALL 64 bit?

(If not, you can't rely on the Virtual Channel Client DLL)

 

Did you check the numerical value of hNative (i.e. not being 0)?

 

Share this post


Link to post
17 minutes ago, rvk said:

Are your connecting client, the used mstsc.exe and RDP-session (including your app) ALL 64 bit?

I'll have to check mstsc and RDP-session after the weekend, but the others are 64-bit. Regardless, the DLL succeeds in enumerating the sources, the scanning progress dialog shows, and it completes the scan; it is only when it reaches the ReadNative method and calls GlobalLock that there's an issue - why would all that comes before it succeed?

 

21 minutes ago, rvk said:

Did you check the numerical value of hNative (i.e. not being 0)?

Yes, it's non-zero.

Share this post


Link to post
Posted (edited)
45 minutes ago, Dave Nottage said:

Yes, it's non-zero.

So the call gives a TWRC_XFERDONE (successful) but the handle could still be invalid.

 

The handle (hNative) is a TW_UINT32 (which is a Cardinal/32bit). I wonder if it shouldn't be a TW_Handle (which should be a NativeUInt and UInt64 on 64 bit). At least GlobalLock expects a THandle (which is UInt64 on 64 bit).

 

Doesn't seem really wise to stuff a handle in an Cardinal/32bit on 64 bit (which like David already mentioned, could be just a simple 32/64 bit truncation error).

 

Strange this worked locally on the machine itself but fails on RDP. Maybe the handle-numbers are much larger and it fails.

 

Edited by rvk
  • Like 1

Share this post


Link to post
On 7/19/2019 at 12:11 AM, rvk said:

The handle (hNative) is a TW_UINT32 (which is a Cardinal/32bit). I wonder if it shouldn't be a TW_Handle (which should be a NativeUInt and UInt64 on 64 bit). At least GlobalLock expects a THandle (which is UInt64 on 64 bit).

You're right, it should be, and I changed it to THandle (same as used by GlobalLock) for hNative in the TransferImages method, and the Handle parameter in the ReadNative method, but to no avail - same problem.

 

On 7/19/2019 at 12:11 AM, rvk said:

Strange this worked locally on the machine itself but fails on RDP. Maybe the handle-numbers are much larger and it fails.

The handle numbers are larger, e.g:

In the "standalone" DLL:   57278728 (36A0108)
In the "RDS" DLL: 2333671976  (8B190228) and 1541866024 (5BE70228)

 

Share this post


Link to post

Then there still could be some other type mismatches in there.

 

Did you also try the TransferMode := ttmMemory; method already?

 

Share this post


Link to post
39 minutes ago, rvk said:

Did you also try the TransferMode := ttmMemory; method already?

Yes, with a different failure; can't recall exactly. I'll check again tomorrow.

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×