Resolved Translate VB6 to C Sharp with DLL

Ryder1912

New member
Joined
Nov 17, 2023
Messages
3
Programming Experience
1-3
Hello, I don't know if the title is correct but that's the idea. I am programming a Payway POS program in the console
Payway POS
that loads a DLL, I was able to do almost all the procedures but there are three that don't work and I need help.
The procedures are almost the same but I am only going to post one because it is very long.

The procedure is called vpiGetBatchCloseData, which gets totals per card once the batch is closed.
This is what the manual says
vpiGetBatchCloseData: Sends the order to obtain a certain record, with the totals per card of the last closure carried out and waits for its response synchronously. To obtain all the records, a cycle must be made from 0 until the response code is different from VPI_MORE_REC.
The application remains waiting until it receives the response or the specified timeout expires.
The port must be open for execution to be successful.

Prototype:
WORD __stdcall vpiGetBatchCloseData(WORD index, vpiBatchCloseDataOut_t* output);

Parameters:
- index: Index of the record that I want to recover
- output: Structure with the registry information I want.

Return: return more values but those are the main 3
- VPI_OK = 0
-VPI_MORE_REC = 1
-VPI_FAIL = 99

This is the code in VB6 (this structure would be the output that is for reference)
VB6:
typedef struct BATCHCLOSEDATA_OUT{
WORD index; // Índice del registro
LPSTR acquirerCode; // Código de procesador
LPSTR batchNumber; // Número de lote
LPSTR issuerCode; // Código de tarjeta
LPSTR purchaseCount; // Cantidad de ventas
LPSTR purchaseAmount; // Monto total de ventas
LPSTR voidCount; // Cantidad anulaciones de venta
LPSTR voidAmount; // Monto total de anulaciones
LPSTR refundCount; // Cantidad de devoluciones venta
LPSTR refundAmount; // Monto total de devoluciones
LPSTR refvoidCount; // Cantidad anulaciones devolución
LPSTR refvoidAmount; // Monto total anul. Devolución
LPSTR date; // Fecha ("DD/MM/AAAA")
LPSTR time; // Hora ("HH:MM:SS")
LPSTR terminalID // Terminal ID
}vpiBatchCloseDataOut_t;

This is the code I want to translate, it is like a summary.
Supposedly it declares an array of 10 of type vpiBatchCloseOut_t and fills it while the While is 1
Code in VB6:
Dim result As Integer
Dim paramOut(0 To 10) As vpiBatchCloseOut_t
Dim recIndex As Integer
 
paramOut.acquirerCode = String(4, vbNull)
paramOut.issuerCode = String(4, vbNull)
paramOut.batchNumber = String(4, vbNull)
paramOut.purchaseCount = String(5, vbNull)
paramOut.voidCount = String(5, vbNull)
paramOut.refundCount = String(5, vbNull)
paramOut.refvoidCount = String(5, vbNull)
paramOut.purchaseAmount = String(13, vbNull)
paramOut.voidAmount = String(13, vbNull)
paramOut.refundAmount = String(13, vbNull)
paramOut.refvoidAmount = String(13, vbNull)
paramOut.terminalId = String(9, vbNull)
result= 0
recIndex= 0
 
While (result = VPI_MORE_REC)
       result= vpiGetBatchCloseData(recIndex,paramOut(recIndex))
       recIndex= recIndex+1
Wend
 
Select Case result
Case VPI_OK
         MsgBox “Comunicación con POS completada en forma exitosa!”
         MsgBox “Se obtuvieron” & Str(recIndex) & “ registros!”
         ProcesarRespuesta(paramOut)
Case VPI_FAIL
         MsgBox “No se pudo enviar el comando!”
         Case VPI_TIME_EXP
         MsgBox “No se recibió respuesta del POS!”
...
End Select

This is the code that I programmed in c sharp
Totales por Tarjeta en C#:
private const ushort VPI_OK = 0;
private const ushort VPI_MORE_REC = 1;
private const ushort VPI_FAIL = 11;
private const ushort VPI_TIME_EXP = 12;
 
public struct vpiBatchCloseDataOut_t
{
    [MarshalAs(UnmanagedType.U2)]
    public ushort index;          // Índice del registro
    [MarshalAs(UnmanagedType.LPStr)]
    public string acquirerCode;   // Código de procesador
    public string batchNumber;    // Número de lote
    public string issuerCode;     // Código de tarjeta
    public string purchaseCount;  // Cantidad de ventas
    public string purchaseAmount; // Monto total de ventas
    public string voidCount;      // Cantidad anulaciones de venta
    public string voidAmount;     // Monto total de anulaciones
    public string refundCount;    // Cantidad de devoluciones venta
    public string refundAmount;   // Monto total de devoluciones
    public string refvoidCount;   // Cantidad anulaciones devolución
    public string refvoidAmount;  // Monto total anul. Devolución
    public string date;           // Fecha ("DD/MM/AAAA")
    public string time;           // Hora ("HH:MM:SS")
    public string terminalId;     // Terminal ID
 
    public vpiBatchCloseDataOut_t([MarshalAs(UnmanagedType.U2)] ushort i, string ac, string bn, string ic, string pc, string pa, string vc, string va, string rc, string ra, string rvc, string rva, string d, string t, string tid)
    {
        this.index = i;           // Índice del registro
        this.acquirerCode = ac;   // Código de procesador
        this.batchNumber = bn;    // Número de lote
        this.issuerCode = ic;     // Código de tarjeta
        this.purchaseCount = pc;  // Cantidad de ventas
        this.purchaseAmount = pa; // Monto total de ventas
        this.voidCount = vc;      // Cantidad anulaciones de venta
        this.voidAmount = va;     // Monto total de anulaciones
        this.refundCount = rc;    // Cantidad de devoluciones venta
        this.refundAmount = ra;   // Monto total de devoluciones
        this.refvoidCount = rvc;  // Cantidad anulaciones devolución
        this.refvoidAmount = rva; // Monto total anul. Devolución
        this.date = d;            // Fecha de la transacción “dd/mm/aaaa”
        this.time = t;            // Hora de la transacción “hh:mm:ss”
        this.terminalId = tid;    // Terminal ID
    }
}
 
// Recuperar información enviada por el POS al realizar una obtención de los totales del cierre de lote
[DllImport("VpiPc.dll", CallingConvention = CallingConvention.StdCall)]
public static extern ushort vpiGetBatchCloseData(ushort index, ref vpiBatchCloseDataOut_t output);
 
// Procedimiento
 
static void obtenerTotalesxTarjeta()
{
    vpiBatchCloseDataOut_t[] paramOut = new vpiBatchCloseDataOut_t[10];
 
    ushort result = 1;
    ushort recIndex = 0;
    ushort recIndexAux = 0;
 
    while (result == VPI_MORE_REC) // Para que entre al while VPI_MORE_REC tiene que ser 1, mientra siga encontrando registros los va llenando o al menos esa es la idea
    {
        paramOut[recIndexAux].index = 0;
        paramOut[recIndexAux].acquirerCode = "";
        paramOut[recIndexAux].issuerCode = "";
        paramOut[recIndexAux].batchNumber = "";
        paramOut[recIndexAux].purchaseCount = "";
        paramOut[recIndexAux].voidCount = "";
        paramOut[recIndexAux].refundCount = "";
        paramOut[recIndexAux].refvoidCount = "";
        paramOut[recIndexAux].purchaseAmount = "";
        paramOut[recIndexAux].voidAmount = "";
        paramOut[recIndexAux].refundAmount = "";
        paramOut[recIndexAux].refvoidAmount = "";
        paramOut[recIndexAux].terminalId = "";
 
        result = vpiGetBatchCloseData (recIndex, ref paramOut[recIndexAux]);
 
        switch (result)
        {
            case VPI_OK:
                logger.Info("Obtener Totales por Tarjeta OK - Registro "+recIndex.ToString());
                ProcesarRespuesta3(paramOut[recIndex]);
                break;
            case VPI_FAIL:
                logger.Info("No se pudo enviar el comando - Registro "+recIndex.ToString());
                break;
            case VPI_TIME_EXP:
                logger.Info("No se recibió respuesta del POS! - Registro " + recIndex.ToString());
                break;
        };
        recIndex++;
        recIndexAux++;
    };
 
    Console.WriteLine("Se obtuvieron " + recIndex.ToString() + " registros!");
 
}

Some lines are in Spanish. That doesn't interest me.
I just want to know if it is programmed correctly.

On the other hand.
Many times I have invoke problems (I have no idea what it is)
I hope you can help me. Thank you in advance.
Regards and I'm sorry for my English.
 
ushort is already an unsigned 2 byte integer. You don't need to mark it up with an attribute that it should be marshalled as a UnmanagedType.U2.

Why are you marking acquireCode as UnmanagedType.LPStr, but not the other strings?

Are you absolutely sure that strings will be ANSI strings, and not Unicode strings?
 
Hello SkyDriver, thanks for replying.

I don't know. At the beginning when I started programming this program I had many problems with PInvokeStackImbalance.
Message = Managed Debug Wizard 'PInvokeStackImbalance': "A call to the PInvoke function 'Payway!Payway.Program::vpiGetBatchCloseData' prevented stack matching. The reason may be that the managed PInvoke signature does not match the unmanaged target signature. Verify that the calling convention and parameters of the PInvoke signature match the target unmanaged signature."

The DLL is written in c++ and the manual doesn't say much. So I did a lot of things by guessing.
This is the Definition of data types (translated into english)
Tipo en API Descripción Tipo en VB6
BYTE 8-bit unsigned integer Byte
LONG 32-bit signed integer Long
LPSTR 32-bit pointer to a string ByVal String
WORD 16-bit unsigned integer Integer


Another of the "problems" that I am having is that the procedures, all of them, if the operation is successful return a 0 (VPI_OK), the ticket is printed and the program closes. I can't display output data (paramOut). Or write that data in a log. I can't stop it, it always exits if it returns 0.

Finally this is how the procedure remained.
CODE:
public struct vpiBatchCloseDataOut_t
{
    public ushort index;           
    public string acquirerCode;   
    public string batchNumber;     
    public string issuerCode;     
    public string purchaseCount;   
    public string purchaseAmount; 
    public string voidCount;       
    public string voidAmount;     
    public string refundCount;     
    public string refundAmount;   
    public string refvoidCount;   
    public string refvoidAmount;   
    public string date;           
    public string time;           
    public string terminalId;     

    public vpiBatchCloseDataOut_t(ushort i, string ac, string bn, string ic, string pc, string pa, string vc, string va, string rc, string ra, string rvc, string rva, string d, string t, string tid)
    {
        this.index = i;           
        this.acquirerCode = ac;   
        this.batchNumber = bn;     
        this.issuerCode = ic;     
        this.purchaseCount = pc; 
        this.purchaseAmount = pa; 
        this.voidCount = vc;       
        this.voidAmount = va;     
        this.refundCount = rc;   
        this.refundAmount = ra;   
        this.refvoidCount = rvc;   
        this.refvoidAmount = rva;
        this.date = d;           
        this.time = t;           
        this.terminalId = tid;   
    }
}

[DllImport("VpiPc.dll", CallingConvention = CallingConvention.StdCall)]
public static extern ushort vpiGetBatchCloseData(ushort index, ref vpiBatchCloseDataOut_t output);


// Procedure
static void TotalsPerCard()
{
    vpiBatchCloseDataOut_t[] paramOut = new vpiBatchCloseDataOut_t[10];

    ushort result = 1;
    ushort recIndex = 0;
    ushort recIndexAux = 0;

    while (result == VPI_MORE_REC)
    {      
        paramOut[recIndexAux] = new vpiBatchCloseDataOut_t(0, "", "", "", "", "", "", "", "", "", "", "", "", "", "");

        result = vpiGetBatchCloseData (recIndex, ref paramOut[recIndexAux]);
      
        switch (result)
        {
            case VPI_OK:
                logger.Info("Totals per card OK - Record "+recIndex.ToString());
                break;
            case VPI_FAIL:
                logger.Info("Could not send command - Record "+recIndex.ToString());                     
                break;
            case VPI_TIME_EXP:
                logger.Info("No response received from POS! - Record " + recIndex.ToString());
                break;
        };
        recIndex++;
        recIndexAux++;
    };

    logger.Info("records were obtained " + recIndex.ToString());

}

Regards.
 
Hello everyone. I was already able to solve the problem.
You just have to assign memory to the variables. Example: paramOut[recIndexAux].acquirerCode = new string(' ', 4);
This is the solution. Regards.


C#:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct vpiBatchCloseDataOut_t
{
    public ushort index;          // Índice del registro
    public string acquirerCode;   // Código de procesador
    public string batchNumber;    // Número de lote
    public string issuerCode;     // Código de tarjeta
    public string purchaseCount;  // Cantidad de ventas
    public string purchaseAmount; // Monto total de ventas
    public string voidCount;      // Cantidad anulaciones de venta
    public string voidAmount;     // Monto total de anulaciones
    public string refundCount;    // Cantidad de devoluciones venta
    public string refundAmount;   // Monto total de devoluciones
    public string refvoidCount;   // Cantidad anulaciones devolución
    public string refvoidAmount;  // Monto total anul. Devolución
    public string date;           // Fecha ("DD/MM/AAAA")
    public string time;           // Hora ("HH:MM:SS")
    public string terminalId;     // Terminal ID   
}
  
// Retrieve information sent by the POS when obtaining the batch closing totals 
[DllImport("VpiPc.dll", CallingConvention = CallingConvention.StdCall)]
public static extern ushort vpiGetBatchCloseData(ushort index, ref vpiBatchCloseDataOut_t output);

static void ProcessResponse(vpiBatchCloseDataOut_t paramOut)
{
            logger.Info("Nro. de Registro " + paramOut.index.ToString());
            logger.Info("Código de procesador " + paramOut.acquirerCode);
            logger.Info("Número de lote " + paramOut.batchNumber);
            logger.Info("Código de tarjeta " + paramOut.issuerCode);
            logger.Info("Cantidad de ventas " + paramOut.purchaseCount);
            logger.Info("Monto total de ventas " + paramOut.purchaseAmount);
            logger.Info("Cantidad anulaciones de venta " + paramOut.voidCount);
            logger.Info("Monto total de anulaciones " + paramOut.voidAmount);
            logger.Info("Cantidad de devoluciones venta " + paramOut.refundCount);
            logger.Info("Monto total de devoluciones " + paramOut.refundAmount);
            logger.Info("Cantidad anulaciones devolución " + paramOut.refvoidCount);
            logger.Info("Monto total anul. Devolución " + paramOut.refvoidAmount);
            logger.Info("Fecha de la transacción " + paramOut.date);  // dd/mm/aaaa
            logger.Info("Hora de la transacción " + paramOut.time);
            logger.Info("Terminal ID " + paramOut.terminalId);
 }

static void GetTotalsbyCards()
 {
     vpiBatchCloseDataOut_t[] paramOut = new vpiBatchCloseDataOut_t[10];

     ushort result = 1;
     ushort recIndex = 0;
     ushort recIndexAux = 0;

            try
            {
                while (result == VPI_MORE_REC)
                {
                    paramOut[recIndexAux] = new vpiBatchCloseDataOut_t();
                      
                    paramOut[recIndexAux].index = recIndexAux;
                    paramOut[recIndexAux].acquirerCode = new string(' ', 4);
                    paramOut[recIndexAux].batchNumber = new string(' ', 4);
                    paramOut[recIndexAux].issuerCode = new string(' ', 4);
                    paramOut[recIndexAux].purchaseCount = new string(' ', 5);
                    paramOut[recIndexAux].purchaseAmount = new string(' ', 13);
                    paramOut[recIndexAux].voidCount = new string(' ', 5);
                    paramOut[recIndexAux].voidAmount = new string(' ', 13);
                    paramOut[recIndexAux].refundCount = new string(' ', 5);
                    paramOut[recIndexAux].refundAmount = new string(' ', 13);
                    paramOut[recIndexAux].refvoidCount = new string(' ', 5);
                    paramOut[recIndexAux].refvoidAmount = new string(' ', 13);
                    paramOut[recIndexAux].date = new string(' ', 11);
                    paramOut[recIndexAux].time = new string(' ', 9);
                    paramOut[recIndexAux].terminalId = new string(' ', 9);
                    
                    result = vpiGetBatchCloseData(recIndex, ref paramOut[recIndexAux]);
                    
                    if (result == VPI_MORE_REC || result == VPI_OK)
                    {
                        logger.Info("Get Totals by Cards OK");
                        ProcessResponse(paramOut[recIndex]);
                    }
                    else
                    {
                        logger.Error("ULC - Error " + result.ToString());
                    }
                    recIndex++;
                    recIndexAux++;
                };
            }
            catch (Exception e)
            {
                logger.Error(e.Message);
            }

        }
 
Back
Top Bottom