0

I want to use functions in dll's via ctype. I can call the function without errors and even the error code of the function is 0 meanig function successfuly finished. But when I try to acces the result variable ist is empty.

I have been implemented the lookup in free pascal severeal years ago and would transfer it to python right now. The interface allow to access via cdel convention and I tied to reimplement in python 3.7.4 with ctypes now

The last working Pascal Prototype have been:

PROCEDURE pGetCallInfo(DriveInfo: pointer; ACall: pointer; AInfo: pointer;
    var AErrorCode: SmallInt); pascal; external 'raccd32a.dll';

My best version in python have been the following:


from ctypes import *
callBookDLL = CDLL('raccd32a')
AInfo = create_string_buffer(400)
err = callBookDLL.cGetCallInfo("self.txt_CallBookPath.text()","DG1ATN",AInfo)

The result ist:

err 0 AInfo.value b''

AInfo should contain a max. 400 char long stringbuffer with an result containing Name, Adress and so on.

As I have a second library I have to acces same way I search for my fault but I was not able to find it. I think my problem is the work with pointer and the type conversion.

I checked teh ctypes howto allready but I can noht solve this trouble.

Thanks a lot so far ...

  • How does the function declaration look like in *C*? *ctypes* doesn't support *pascal* calling convention (assuming your *.dll* is *32bit*). – CristiFati Aug 09 '19 at 21:52
  • I never uses the function in C, I think that is even my problem to convert the function. As well as I did not understand the Manual in that case. They did not give a header file they try to explain. I put the explanations on my server, to be found at http://awi5.maschinenbau.tu-ilmenau.de/RACCD32a.txt Thanks so far ... – TobbY DG1ATN Aug 10 '19 at 07:25
  • I'd suggest to edit your question and add all the required details (for example how do you call the function from *Delphi*). Check [\[SO\]: How to create a Minimal, Reproducible Example (reprex (mcve))](https://stackoverflow.com/help/mcve) for more details. – CristiFati Aug 12 '19 at 09:14

1 Answers1

0

Check [Python 3.Docs]: ctypes - A foreign function library for Python. It contains (almost) every piece of info that you need.

There are a number of problems:

  1. ctypes doesn't support pascal calling convention, only cdecl and stdcall (applies to 32bit only). That means (after reading the manual) that you shouldn't use the p* functions, but the c* (or s*)

  2. You didn't specify argtypes (and restype) for your function. This results in UB. Some effects of this:

  3. It is a procedure (a function that returns void). Anyway this is a minor one

Here's some sample code (of course it's blind, as I didn't test it):

#!/usr/bin/env python3

import sys
import ctypes


dll = ctypes.CDLL("raccd32a.dll")

cGetCallInfo = dll.cGetCallInfo
cGetCallInfo.argtypes = [ctypes.c_char_p, ctypes.c_char_p, ctypes.c_char_p, ctypes.POINTER(ctypes.c_short)]
cGetCallInfo.restype = None

ADriveInfo = self.txt_CallBookPath.text().encode()
#ADriveInfo = b"C:\\callbook2019\\"  # Notice the double bkslashes
ACall = b"DG1ATN"
AInfo = ctypes.create_string_buffer(400)
result = ctypes.c_short(0)
cGetCallInfo(ADriveInfo, ACall, AInfo, ctypes.byref(result))

@EDIT0:

From the beginning, I wanted yo say that the 1st argument passed to the function doesn't make much sense. Then, there are problems regarding the 2nd one as well. According to the manual ([AMT-I]: TECHNICAL INFORMATION about RACCD32a.DLL (emphasis is mine)):

ADriveInfo, ACall and AInfo are pointers to zero-terminated strings. These strings has to exist at the moment of calling xGetCallInfo. The calling program is responsible for creating them. AInfo must be long enough to comfort xGetCallInfo (at least 400 characters). Note: "Length of AInfo" refers to the length of the string AInfo points at. ADriveInfo and ACall are treated in the same manner for short.

In ADriveInfo the procedure expects the path to the CD ROM drive. Use "G:\" if "G:" designates the CD ROM drive with the callbook CD ROM. Keep in mind that this information is a *must* and the calling program has to know it. Note: If the active directory on drive G: is not the root, ADriveInfo = "G:" will lead to an error 3. So always use "G:\". The calling program has to ensure that the length of ADriveInfo does not exceed 80 characters.

ACall contains the call you are looking for, all letters in lower case, no additional spaces etc. The calling program has to ensure that ACall is not longer than 15 characters. However, there is no call longer than 6 characters in the database.

CristiFati
  • 38,250
  • 9
  • 50
  • 87
  • Thanks, It looks better but I get another error code now. I have to check with other data cd first but I could not understand why it works with pascal and this dll an not with python. I get error code **-3: Wrong CD: DLL does not match the CD inserted- ask for a new DLL** now. If I cange the path (parameter 1), then I got error code **3: Path not found** so it look like the parameter will be ok. I check an it have been the dll version deliverd with the used CD. – TobbY DG1ATN Aug 10 '19 at 21:38
  • And one more question, why AInfo had not been marked as ctypes.byref()? The content of this variable should also be changed by the dll? – TobbY DG1ATN Aug 10 '19 at 21:41
  • The buffer is mutable (it's in the link I provided). – CristiFati Aug 10 '19 at 21:51
  • What happens if from pascal you use `PROCEDURE cGetCallInfo(DriveInfo: pointer; ACall: pointer; AInfo: pointer; var AErrorCode: SmallInt); cdecl; external 'raccd32a.dll';`? – CristiFati Aug 10 '19 at 21:53
  • I am on vacation right now and I will answer tonight. But I did not have a pascal IDE or Debugger installed on this Laptop. I will send you how I use the result from the code an I know that the Software had been working in July last time. As I have to integrate new functionality and I had to use python at work I started a redesign during my holiday trip. – TobbY DG1ATN Aug 11 '19 at 07:31
  • I just checked the Pascal code. There I only evaluate the error code. I am currently only interested in whether ACall is present in the database or not. Here the code excerpt: pGetCallInfo({at}DriveInfo, {at}Call, {at}Info, ErrorCode); if ErrorCode > 0 then memo1.Lines.Add(format('%-20s => %d',[strs[i],ErrorCode])); if (ErrorCode = 0) then do something, And with DG1ATN oder EF1W I got 0 and now I got -3 – TobbY DG1ATN Aug 11 '19 at 20:56
  • I chech with the seconde dll (uses stdcall and it works perfect, I will contact the autor off the first and check if there is a problem with the dll. When I got the answer I will come back to this tread and close or ask again – TobbY DG1ATN Aug 12 '19 at 09:31
  • The first argument was an qt5 lineEdit field where I had my local path in ...it has a length of 62 char. I use it with **self.txt_CallBookPath.text().encode()** ... perhaps the hint was good, because i might remember that ther is no whitespace allowed, but it still not work, The ACall normaly came from a mysql request so I use my own call DG1ATN only for testting and discribing. For the last test I used as ADriveInfo b"C:\callbook2019\" – TobbY DG1ATN Aug 13 '19 at 07:25
  • I'd suggest (as I commented on the question) to put all relevant info in the question, instead of scattering it in comments. – CristiFati Aug 13 '19 at 11:36
  • I tried again @home and your solution works perfect. I think the database should not be on drive c: it works on drive h: (Networkdrive) – TobbY DG1ATN Aug 19 '19 at 18:37