views:

91

answers:

1

I have to access a POS terminal under ms windows xp. I am using python 2.7. The crucial function in the DLL I load that does the payment accepts two pointer to structures, but it crashes returning 1 (Communication error) but without further messages. Please note that when the payment function is called, not all the elements of POSData structure receive a value. Other function I tried (GetVersion) does work. Here specifications and my code:

typedef struct
{
  char IPAddress[16]; //xxx.xxx.xxx.xxx
  int Port;
} TETHParameters;   
typedef struct
{
  char TerminalId[8+1];
  char AcquirerId[11+1];
  char TransactionType[3+1];
  char TransactionResult[2+1];
  char KODescription[24+1];
  char CardType[1+1];
  char STAN[6+1];
  char PAN[19+1];
  char AuthorizationCode[6+1];
  char OperationNumber[6+1];
  char DataTrs[7+1];
} TPOSData;

typedef struct
{
  char Amount[8+1];
  char ECRId[8+1];
  char PaymentType[1+1];
  char TerminalId[8+1];
} TECRData;

__declspec(dllexport) void IAE17_GetVersion(char *Version);
__declspec(dllexport) void IAE17_InitEth(TETHParameters *ETHParameters);

__declspec(dllexport) void IAE17_Free(void);

__declspec(dllexport) int IAE17_Payment(TECRData *ECRData, TPOSData *POSData);

from ctypes import *
#da python 3.x sara' configparser
import ConfigParser  
import logging
from time import  localtime,  strftime

    #STRUTTURE DATI
class TETHParameters(Structure):
    _fields_ =  [("IPAddress" , c_char_p), ("Port" , c_int )]


class TECRData(Structure):
    _fields_ = [("Amount" , c_char_p),
    ("ECRId", c_char_p),
    ("PaymentType", c_char_p),
    ("TerminalId", c_char_p),
    ("Contract", c_char_p),
    ("PreauthorizationCode", c_char_p),
    ("STAN", c_char_p),
    ("Ticket2Ecr", c_char_p)]


class TPOSData(Structure):
    _fields_ = [
    ("TerminalId" , c_char_p),
    ("AcquirerId" , c_char_p),
    ("TransactionType" , c_char_p),
    ("TransactionResult" , c_char_p),
    ("KODescription" , c_char_p),
    ("CardType" , c_char_p),
    ("STAN" , c_char_p),
    ("POSBalance" , c_char_p),
    ("BankBalance" , c_char_p),
    ("PAN" , c_char_p),
    ("AuthorizationCode" , c_char_p),
    ("OperationNumber" , c_char_p),
    ("AmountAuth" , c_char_p),
    ("PreauthorizationCode" , c_char_p),
    ("ActionCode" , c_char_p),
    ("DataTrs" , c_char_p),
    ("AmountEcho" , c_char_p),
    ("Ticket" , c_char_p)
    ] 

ECRData = TECRData( ECRId = c_char_p( '012345678' ), 
                    Amount  = c_char_p( '00000000')  , 
                    TerminalID = c_char_p( '01234567' ), 
                    PaymentType = c_char_p ("0")
                       )    


POSData = TPOSData( KODescription = c_char_p('                        '),
                            TerminalId = c_char_p('        '),  
                            AcquirerId = c_char_p('           '), 
                            TransactionType = c_char_p('   '), 
                            TransactionResult = c_char_p('   '),
                            CardType = c_char_p('  '), 
                            STAN = c_char_p('      '),
                            PAN = c_char_p('                   '), 
                            AuthorizationCode = c_char_p('      '),
                            OperationNumber = c_char_p('      '), 
                            DataTrs = c_char_p('       ')  
                            )   
ETHParameters = TETHParameters( IPAddress = c_char_p( '192.168.127.190' ) ,  Port = c_int(45119))                           
iae17 = windll.LoadLibrary('iae17')     
iae17.IAE17_InitEth( byref( ETHParameters) )   
result =  iae17.IAE17_Payment( byref(ECRData), byref(POSData))                      
print result
+1  A: 

c_char_p is a direct translation of a C's char *. So, it seems to me that while your C structure is

typedef struct
{
  char TerminalId[8+1];
  char AcquirerId[11+1];
  char TransactionType[3+1];

&c

the allegedly-corresponding one you're making in ctypes is, instead, equivalent to

typedef struct
{
  char* TerminalId;
  char* AcquirerId;
  char* TransactionType;

&c

which is of course a drastically different thing. Why are you using "pointers" instead of ctypes' arrays? I don't understand -- thanks in advance for clarifying!

Alex Martelli
I used c_char_p by mistake, of course. But I read that c_char is 1 byte long and, as I believe, multiplying it produces arrays, which my function doesn't want.Anyway, after your kind answer, I rewrote the definitions to look like this one class TECRData( Structure ): _fields_ = [("Amount" , c_char * 9 ), ("ECRId", c_char * 9 ),...]and instantiated ECRData and POSData like this:ECRData = TECRData( ECRId = '012345678', .. PaymentType = "0" ) but the script waits two-three seconds before to crash).Any other suggestion, please?
marcocassisa
Those arrays of chars, I'd bet, want a terminating zero (don't you have docs about that? anyway, their cutesy `+1` length declarations are a strong hint) -- so setting `ECRId` to 9 characters without any terminating 0 is looking for trouble. `create_string_buffer`, http://docs.python.org/library/ctypes.html#ctypes.create_string_buffer , takes care of that *and* of the issue that such arrays of chars in C are often required to be mutable while a Python string is never mutable.
Alex Martelli
Thanks for taking care. Sorry for my dumbness, but if I use create_string_buffer to instantiate, can't use c_char in declaration because of a TypeError (c_char_array_9 instead of c_char). I tried also to initialize the vars this way POSData = TPOSData(KODescription = '\x00'*25 ...) and the script don't crash anymore (I also rewrote the ethparameter address ending it with \000). However, maybe because of immutability of strings, POSData.KODescription is TypeNone... I think I made a step forward, but I am here again...
marcocassisa
@marco, since you must use `create_string_buffer` to populate a struct, then the various `type(create_string_buffer(N+1))` are what you must use in the `_fields_` entries of your `Structure`s. Whether Python crashes or not when C code writes over a (theoretically immutable) string is somewhat arbitrary, but even if you're "lucky" and it doesn't crashes, you won't get results reliably this way, as you've seen.
Alex Martelli
Is type(create_string_buffer(int)) undocumented as definition for the _fields_ entries? Anyway, <code>type(POSData.TerminalId) before and after the call to the DLL gives <str> and its repr is '' even if POSData is an instance of TPOSData defined as follow: TPOSData(Structure): _fields_ = [("TerminalId" = type(create_string_buffer(9)))] and instantiated as POSData = TPOSData(). Mr. Martelli, your patience is amazing, I am grateful
marcocassisa
The `=` in your `("TerminalId" = type...` is a mistake, and I'm surprised you didn't get a syntax error out of it! It should, instead, be `("TerminalId", type...` -- comma, not equals.
Alex Martelli
A mistake done by rewriting the code, it is. Sure, it would have raised a syntax error. Anyway, it seems that I am not lucky with my code, the function refuses to write in the elements of my structure `TPOSData`... But I noticed that they are considered as `<str>` and no more as `TypeNone` after the call to the function in the Dll. Being string I can't get their `raw` value to see if there are still the `\x00` generated by `create_string_buffer` inside
marcocassisa
Alex Martelli