
CAPÍTULO 1. Invitados inesperados
Todo comenzó esa desafortunada mañana cuando Project Manager anunció que los plazos de implementación del proyecto deberían reducirse rápida y decisivamente en un mes. Más precisamente, el proyecto debería estar listo en 4 días. No, nuestro PO no es una bestia, y no se parece en absoluto a un
búho (tal vez solo un poco como un cuervo), simplemente sucedió. Bueno, si es necesario, es necesario, especialmente porque al equipo (y yo soy el desarrollador líder del equipo "C") se le prometió algo sabroso. El reloj y el calendario eran jueves, 11:00, para el lunes, el proyecto debería estar listo.
Para empezar, ¿qué estamos haciendo? Nos dedicamos a la automatización de cines: control automático y remoto de equipos, automatización de proyección de películas, monitoreo, paneles de video y ahora también terminales para la venta de boletos y bares. Específicamente, el último párrafo está dedicado a este artículo.
El proyecto en sí, que tuvo que completarse antes del lunes, es una especie de capa entre el servidor principal de Scala y el terminal de pago de hierro VeriFone VX 820 (de hecho, hay más terminales, pero solo tómalo como ejemplo). Está claro que nadie nos dará transacciones a través de él así, por lo tanto, se utilizan las utilidades y bibliotecas de Sberbank / Arcus y UCS. Por lo tanto, el esquema de trabajo debería ser el siguiente:

Exteriormente, se ve así:

Además, este subsistema debe usarse en cajas registradoras estándar que todos vieron en cualquier sala de cine en los cajeros.
Según la tradición interna, llamamos a cada proyecto de nuestro equipo un nombre de la mitología nórdica antigua, para este subsistema se eligió el nombre Gefjon: el nombre de la diosa de la fertilidad y la fertilidad (un buen nombre para un servidor de pago, ¿no es así? Bueno, la leyenda sobre los toros que cortan la isla encaja idealmente con la arquitectura actual, cortar el trabajo con equipos de un lenguaje de alto nivel).
El formato de los mensajes entrantes y salientes es un servidor HTTP con una carga JSON. Este es el compromiso óptimo entre Scala, que es difícil pasar a aislar datos binarios de flujos de socket y C, que es difícil subir a transferir objetos a través de la red. No hay muchas operaciones posibles que necesiten ser operadas: pago, cancelación, devolución, varios tipos de informes, abrir el menú de servicio y hacer ping. No se ve nada complicado. Como hay tres sistemas bancarios (y se espera una reposición posterior de la familia), se decidió dividir el proyecto en componentes:

Los bloques verdes son los que teníamos que hacer, los azules son los que no se pueden cambiar y que proporciona el banco.
Dado que los principales problemas surgieron solo con el software de Sberbank, el artículo en su conjunto estará dedicado a las trampas, que contamos con nuestro barco.
CAPÍTULO 2. Cordero asado

(foto: heaclub.ru)
... se parece a esto. El código para ese prototipo, que fue escrito hace unos meses para dejar en claro a todas las personas superiores que podemos trabajar con aplicaciones bancarias, se veía casi igual.
char buf[BUF_KB * 2]; char * null; char * grep; #ifdef _WIN32_WINNT char * ptr; null = "nul"; grep = "findstr"; #else null = "/dev/null"; grep = "grep"; #endif sprintf(buf, "%s %"PRIi32"= %sops.ini >%s 2>%s || " "echo %"PRIi32"=9,6,PINPAD_TEST >> %sops.ini", grep, TERM_ARCUS_TEST_PINPAD, TERM_PATH, null, null, TERM_ARCUS_TEST_PINPAD, TERM_PATH); #ifdef _WIN32_WINNT ptr = buf; while (*ptr) { if (*ptr == '/') *ptr = '\\'; ptr++; } #endif
Está claro que esto no era adecuado para la versión de producción, por lo que era esencialmente necesario escribir todo de nuevo.
Cada banco que proporciona bibliotecas para trabajar con el terminal generalmente proporciona dos opciones de conexión: a través de las funciones de la biblioteca (.so / .dll) o mediante una utilidad preparada que solo necesita pasar dos valores: el tipo de operación y la cantidad (cuando sea necesario). En teoría, nada complicado, solo algo
char buffer[100]; sprintf(buffer, "%d %d", atoi(argv[1]), atoi(argv[2])); system(buffer);
El resultado de la operación se colocará en el archivo "e", y la verificación de deslizamiento en el archivo "p". Simplemente envíe estos archivos a stdout con conversión a JSON, para que el servidor HTTP los envíe como carga útil sin pensar en lo que hay allí.
Pero este artículo no habría sido publicado si todo hubiera sido tan simple.
CAPÍTULO 4. A través de la montaña y debajo de la montaña.
La versión inicial de la implementación fue una simple llamada de aplicación: el servidor HTTP llamó al contenedor requerido con parámetros unificados (por ejemplo, el informe X es 4), y la utilidad, por ejemplo gfj_pilot, lanzó sb_pilot con el parámetro requerido para esta operación (por ejemplo, el informe X es 9) . Luego, la utilidad de envoltura leyó el resultado de la operación del archivo electrónico (por ejemplo, 2000 - "pago rechazado, repita la operación") y lo convirtió en un error universal (por ejemplo 3 - "Error al leer o procesar la tarjeta / cuenta, repita la operación"). Después de eso, el archivo "p" se convirtió a base64 para evitar que se rompa el formato y se envió junto con el resultado a stdout como JSON.
Todo esto funcionó perfectamente hasta que en un buen momento nos informaron que ...
... esto no funciona en Windows.

Bueno, más precisamente, Windows no tiene problemas (excepto que el deslizamiento se genera en la codificación Cp-1251 y la consola funciona en CP866). El archivo "e" simplemente no se generó. Lanzó una utilidad bancaria directamente:
C:\banks\sber\sb_pilot>dir C . : B401-6B9D C:\banks\sber\sb_pilot 04.02.2019 12:28 <DIR> . 04.02.2019 12:28 <DIR> .. 31.01.2019 17:12 10 832 F12X24.BIN 31.01.2019 17:12 128 000 gate.dll 31.01.2019 17:12 72 192 loadparm.exe 31.01.2019 17:12 36 204 OPT0.R 31.01.2019 17:12 20 716 OPT1.R 31.01.2019 17:12 1 806 OPT3.R 31.01.2019 17:12 388 608 pilot_nt.dll 31.01.2019 23:06 463 pinpad.ini 31.01.2019 17:12 91 136 posScheduler.exe 31.01.2019 17:12 418 printers.ini 01.02.2019 16:51 91 646 sbkernel1902.log 31.01.2019 17:12 653 312 sbrf.dll 31.01.2019 17:12 840 192 SBRFCOM.dll 31.01.2019 17:12 3 142 656 sb_kernel.dll 01.02.2019 16:51 9 SESS.D 01.02.2019 16:51 715 SPLC.D 31.01.2019 17:12 72 192 upwin.exe 20 5 659 718 2 37 567 004 672 # (1) 10 (1000 ) C:\banks\sber\sb_pilot>loadparm.exe 1 1000 C:\banks\sber\sb_pilot>dir C . : B401-6B9D C:\banks\sber\sb_pilot 04.02.2019 12:28 <DIR> . 04.02.2019 12:28 <DIR> .. 04.02.2019 12:28 216 commerr.log 31.01.2019 17:12 10 832 F12X24.BIN 31.01.2019 17:12 128 000 gate.dll 31.01.2019 17:12 72 192 loadparm.exe 31.01.2019 17:12 36 204 OPT0.R 31.01.2019 17:12 20 716 OPT1.R 31.01.2019 17:12 1 806 OPT3.R 01.02.2019 18:51 1 349 p 31.01.2019 17:12 388 608 pilot_nt.dll 31.01.2019 23:06 463 pinpad.ini 31.01.2019 17:12 91 136 posScheduler.exe 31.01.2019 17:12 418 printers.ini 04.02.2019 12:28 92 218 sbkernel1902.log 31.01.2019 17:12 653 312 sbrf.dll 31.01.2019 17:12 840 192 SBRFCOM.dll 31.01.2019 17:12 3 142 656 sb_kernel.dll 01.02.2019 16:51 9 SESS.D 01.02.2019 16:51 715 SPLC.D 31.01.2019 17:12 72 192 upwin.exe 19 5 659 029 2 37 567 008 768 C:\banks\sber\sb_pilot>
De hecho, no hay un archivo "e". Piedra hacia Sberbank # 1. Estamos escribiendo una carta a Sberbank (posteriormente recibimos una respuesta de que debería ser así), y dado que no hay tiempo para la correspondencia y tenemos que comenzar ahora, estamos buscando soluciones para obtener el resultado.
04.02 12:28:55 SBKRNL: Failed to open device \\.\COM1, err 2 04.02 12:28:56 SBKRNL: Failed to open device \\.\COM1, err 2 04.02 12:28:56 SBKRNL: Result = 0 04.02 12:28:56 GATE: unlock:'00000054' 04.02 12:28:56 GATE: lock:'00000054' 'UPOSWINMUTEX2' 04.02 12:28:56 GATE: unlock:'00000054' 04.02 12:28:56 LOADPARM: Unloading GATE.DLL... 04.02 12:28:56 GATE: SB_KERNEL.DLL is unloaded 04.02 12:28:56 LOADPARM: GATE.DLL unloaded
Sí, el resultado se puede obtener del registro sbkernel.log. Inconvenientemente, además no hay un hash de mapa para luego atornillar "Gracias" de Sberbank. No bueno
Deberá conectarse a la biblioteca pilot_nt.dll e importar funciones desde ella. Todo estaría bien, pero ... Una piedra hacia Sberbank # 2: no existe tal biblioteca en Linux, tendrá que crear dos aplicaciones diferentes para diferentes plataformas: para Linux, llame a la utilidad sb_pilot (similar a loadparm.exe, por cierto piedra # 3 para un nombre de utilidad diferente en diferentes plataformas ), en Windows, conéctese a la biblioteca pilot_nt.dll.
CAPÍTULO 5. Acertijos en la oscuridad
A las 19:00 horas.
Sberbank es una gran empresa, la mayoría de las soluciones de software se producen de acuerdo con GOST y documentos formales. Entramos en el catálogo que Sberbank suministra con las bibliotecas:
Sberbank$ ls -l Docs 30160 drwx------ 2 alex alex 4096 17 19:31 FAQ -rw-rw-r-- 1 alex alex 3398465 9 2018 UPOS ().docx -rw-rw-r-- 1 alex alex 1182078 9 2018 UPOS .docx -rw-rw-r-- 1 alex alex 853504 9 2018 .doc drwx------ 3 alex alex 4096 31 17:11 -rw-rw-r-- 1 alex alex 5280787 9 2018 POS-.docx -rw-rw-r-- 1 alex alex 1149640 9 2018 .docx drwx------ 2 alex alex 4096 28 2018 UPOS drwx------ 2 alex alex 4096 28 2018 -rw-rw-r-- 1 alex alex 3451601 9 2018 ().docx -rw-rw-r-- 1 alex alex 1956196 9 2018 .docx -rw-rw-r-- 1 alex alex 1043161 9 2018 ()_().docx -rw-rw-r-- 1 alex alex 4348157 9 2018 POS-.docx -rw-rw-r-- 1 alex alex 3970267 9 2018 .docx drwx------ 3 alex alex 4096 28 2018 -rw-rw-r-- 1 alex alex 2644702 9 2018 POS-.docx drwx------ 2 alex alex 4096 28 2018 -rw-rw-r-- 1 alex alex 1558211 9 2018 .png
Mucho bien, pero solo nos interesa el directorio para desarrolladores:
Sberbank$ ls -l Docs/\ \ \ / 8704 -rw-rw-r-- 1 alex alex 47105 9 2018 1C.docx -rw-rw-r-- 1 alex alex 1824 9 2018 cardtype.h -rw-rw-r-- 1 alex alex 2590378 9 2018 cr_ttk_protocol_ru.rtf -rw-rw-r-- 1 alex alex 208 9 2018 deprtmnt.h -rw-rw-r-- 1 alex alex 16681 9 2018 errors.h drwx------ 6 alex alex 4096 28 2018 examples -rw-rw-r-- 1 alex alex 58575 9 2018 gate.h -rw-rw-r-- 1 alex alex 4218 9 2018 paramsln.h -rw-rw-r-- 1 alex alex 61693 9 2018 pilot_nt.h -rw-rw-r-- 1 alex alex 28160 9 2018 ReadTrack2.doc -rw-rw-r-- 1 alex alex 7417 9 2018 sbkernel.h -rw-rw-r-- 1 alex alex 144896 9 2018 sb_pilot.doc -rw-rw-r-- 1 alex alex 3525323 9 2018 ole- sbrf.dll.rtf -rw-rw-r-- 1 alex alex 46683 9 2018 gate.dll.chi -rw-rw-r-- 1 alex alex 255414 9 2018 gate.dll.chm -rw-rw-r-- 1 alex alex 814653 9 2018 gate.dll.pdf -rw-rw-r-- 1 alex alex 41618 9 2018 pilot_nt.chi -rw-rw-r-- 1 alex alex 241716 9 2018 pilot_nt.chm -rw-rw-r-- 1 alex alex 968753 9 2018 pilot_nt.pdf -rw-rw-r-- 1 alex alex 81 9 2018 .txt
Una gran cantidad de papel usado, por si acaso, vuelva a leer pilot_nt, del cual aprendemos lo siguiente:
Tabla 1. Sistema operativo sb_pilot compatible.
Resulta que la utilidad para Windows todavía debería llamarse sb_pilot. Bueno, una piedra hacia Sberbank # 4 por la inconsistencia de su propia documentación.
Transferencia de los resultados del programa.
Al final del programa, se forman dos archivos de texto: el archivo de intercambio y el archivo de verificación.
El primero se llama e y está destinado a pasar parámetros del funcionamiento perfecto al programa de llamada. La primera línea de este archivo contiene el código del resultado de la operación y un texto separado por comas que explica el mensaje de texto. El código 0 significa pago exitoso, cualquier otro valor: rechazo o incapacidad para realizar el pago.

Perezosamente tiramos una piedra más y comenzamos a estudiar la documentación para conectar la biblioteca directamente.
El procedimiento para llamar a funciones de biblioteca
Al pagar (devolver) compras con tarjeta de crédito, el programa de efectivo debe llamar a la función card_authorize () de la biblioteca de Sberbank completando los campos TType y Amount y especificando valores cero en los campos restantes. Al final de la función, debe analizar el campo RCode. Si contiene el valor "0" o "00", la autorización se considera completada con éxito, de lo contrario se rechaza. Además, debe verificar el valor del campo Verificar.
Si no es NULL, debe enviarse para imprimir (en modo no fiscal) y luego
eliminar llamando a la función GlobalFree (). Al cerrar un turno, el programa de efectivo debe llamar a la función close_day () de la biblioteca Sberbank completando el campo TType = 7 y especificando valores cero en los campos restantes. Al final de la función, verifique el valor del campo Verificar.
Si el campo Verificar no es NULL, debe enviarse para imprimir (en modo no fiscal) y luego eliminarse llamando a la función GlobaFree ().
Suena fácil, incluso se proporciona un archivo de encabezado. Bueno, conéctalo, compila y ...
$ cat main.c && i686-w64-mingw32-gcc main.c -o main.a #include "pilot_nt.h" int main(void) { return 0; } In file included from main.c:1:0: pilot_nt.h:525:3: error: unknown type name 'auth_answer' auth_answer ans; ^ pilot_nt.h:544:3: error: unknown type name 'auth_answer' auth_answer ans; ^ pilot_nt.h:567:3: error: unknown type name 'auth_answer' auth_answer ans; ^ pilot_nt.h:590:3: error: unknown type name 'auth_answer' auth_answer ans; ^ pilot_nt.h:627:3: error: unknown type name 'auth_answer' auth_answer ans; ^ pilot_nt.h:668:3: error: unknown type name 'auth_answer' auth_answer ans;
Ummm ... que? Abra pilot_nt.h:
#ifdef __cplusplus extern "C"{ #endif <...> /** * * , . */ struct auth_answer{ int TType; /**< [in] . ::OpetationTypes */ unsigned long Amount; /**< [in] */ char RCode[3]; /**< [out] */ char AMessage[16]; /**< [out] */ int CType; /**< [in,out] */ char* Check; /**< [out] , GlobalFree */ }; <...> struct auth_answer7{ auth_answer auth_answ; /**< [in, out] . . ::auth_answer */ <---- THIS char AuthCode[MAX_AUTHCODE]; /**< [out] . 7 . */ char CardID [CARD_ID_LEN]; /**< [out] . 25 . */ int SberOwnCard; /**< [out] */ };
Inmediatamente, sin mirar la piedra para comentarios en ruso en la codificación CP1251.
Bueno, la piedra más seria: queridos desarrolladores de C ++. Si escribe "C" externa, esto significa que el compilador de C debe compilar el código dentro del bloque. Si NO ha realizado un `typedef` de una estructura, cada vez que lo mencione como referencia de tipo, debe escribir la palabra clave` struct`.
Parchee el archivo para desarrolladores, sustituyendo la palabra `struct` donde sea necesario. Vinculación a la biblioteca `pilot_nt.dll`. Victoria, no? Lanzamos nuestra aplicación.
CAPÍTULO 6. Del fuego a las llamas
Bueno, lo entiendes, ¿verdad? La aplicación simplemente falla. Hasta el principal. Medite, agregue el análogo NIH de la función errno para Windows: GetLastError (piedra # 3 hacia Microsoft, las dos primeras son para codificaciones).
C:\banks\sber\WIN>sb_pilot.exe 1 1000 E: !g_sblibrary (0xc0000096)
0xc0000096? ¿No debería GetLastError devolver un código de error adecuado?
Para obtener una lista completa de los códigos de error proporcionados por el sistema operativo, consulte Códigos de error del sistema.
Sí, abre el artículo
aquí :
Los siguientes temas proporcionan listas de códigos de error del sistema. Estos valores se definen en el archivo de encabezado WinError.h.
- Códigos de error del sistema (0-499) (0x0-0x1f3)
- Códigos de error del sistema (500-999) (0x1f4-0x3e7)
- Códigos de error del sistema (1000-1299) (0x3e8-0x513)
- Códigos de error del sistema (1300-1699) (0x514-0x6a3)
- Códigos de error del sistema (1700-3999) (0x6a4-0xf9f)
- Códigos de error del sistema (4000-5999) (0xfa0-0x176f)
- Códigos de error del sistema (6000-8199) (0x1770-0x2007)
- Códigos de error del sistema (8200-8999) (0x2008-0x2327)
- Códigos de error del sistema (9000-11999) (0x2328-0x2edf)
- Códigos de error del sistema (12000-15999) (0x2ee0-0x3e7f)
Ok, tenemos un error indocumentado, tiramos una piedra y abrimos el omnisciente google:
La esencia del error es que alguna subrutina usa una de las instrucciones
- _inp ()
- _inpw ()
- _inpd ()
- _outp ()
- _outpw ()
- _outpd ()
Su uso está prohibido en los núcleos NT, ya que intentan trabajar directamente con el puerto paralelo. Aparentemente, este código se llama en el inicializador de la biblioteca, es decir la biblioteca en el inicio quiere sondear los puertos en busca de dispositivos, pero el núcleo NT requiere trabajar a través del controlador.
Situación desesperada?
CAPÍTULO 8. Arañas y moscas
10 p.m. Por si acaso, surge la idea de verificar que esto no se deba al hecho de que usamos compilación cruzada con Linux usando mingw. Al mismo tiempo, entendemos que Sberbank ofrece solo una aplicación de 32 bits, por lo que no funcionará con una aplicación de 64 bits, está bien, pero aún así lanzaremos una piedra hacia Sberbank para la versión de solo 32 en 2019.
Dado : instalado en virtualbox windows 7;
Requerido : Instale Visual Studio y copie MVP.
Vamos al sitio web de Microsoft, descargamos Visual Studio 2017. Tomamos la licencia comunitaria, ya que la tomamos para verificación, para los negocios, la licencia se comprará si despega.
Descarga unos cientos de megabytes y ...
Vemos que nuestra versión del sistema operativo (Windows 7) no es compatible.
Ok, vamos a todo tipo de sitios obscenos, estamos buscando Visual Studio 2008, descargamos unos cientos de megabytes nuevamente y ...
Obtenemos el archivo iso.
Bien, intentemos instalar Daemon Tools 10 (ya que esta es la versión que ofrece el sitio) para insertar este disco virtual.
Ejecute el binario descargado. Error de encendido, requiere .NET Framework 4.5, descargar, instalar.
Iniciamos el binario descargado, la instalación ha comenzado, el gestor de arranque dice que necesita 4.5.2, descargar, instalar.
Iniciamos el binario descargado, la instalación ha comenzado, el gestor de arranque dice que no irá a ningún lado hasta que instalemos la actualización de seguridad KB3033929, la descarguemos, la instalemos.
Y recibimos una bofetada de Microsoft como mensaje:

Lanzamos violentamente una piedra muy afilada hacia Microsoft, descargamos las antiguas herramientas Daemon de torrents, desempaquetamos con éxito Visual Studio, instalamos, finalmente (00:00) compilamos MVP, obtenemos el mismo error. Bueno, había una buena versión, pero no creció juntos.
CAPÍTULO 11. En el umbral
Estamos escribiendo al segundo programador, que en este momento completa con urgencia el servidor y el procedimiento de registro. Él recuerda que hay un
repositorio git que se conecta a esta biblioteca en NT y funciona con él.
Mirando sospechosamente el repositorio, descárguelo, compílelo y ejecútelo. Funciona

Observamos el código aún más sospechosamente. El código es idéntico, excepto que está escrito en C ++ y no en C.
Entendemos que el lenguaje no tiene nada que ver con eso. Observamos las bibliotecas del Sberbank, que extrae el código.
Vemos el último compromiso.
Y aquí estamos esperando otra sorpresa.
Resulta que las versiones de la biblioteca Sberbank pueden ser diferentes. La última confirmación aumenta la versión del 23 al 27. Copie la versión del gita a su computadora de prueba - ¡FUNCIONA!
Verificamos todos los archivos enviados por Sberbank, comparamos las versiones y construimos una tableta:
Genial, ahora vamos a sanar. En aquellos sistemas donde cuesta 26, actualice a 29 o 27 y todo despegará.
Lanzamos la piedra # 9 hacia Sberbank por romper el comportamiento en los sistemas NT.
CAPÍTULO 12. Lo que les esperaba dentro
¿No hay suficiente e file? No importa, tomamos encabezados parcheados, enlazamos dinámicamente a la biblioteca para devolver un error correctamente, escribimos un código que simplemente escribe el código de retorno de la función en el archivo "e", llamemos al biner sb_pilot.exe y ...
Para trabajar, funciona.
Esa es solo la versión para el sistema "Cryptor" no crea un archivo "p".
Observamos con tristeza la sangre que gotea sobre los nudillos y la abolladura en la pared.Para empezar, ¿qué es el sistema Cryptor?
Cryptera es una empresa danesa que produce equipos de cifrado / equipos de seguridad / claves, etc. Creo que todos vieron una de las copias de sus productos:

Entonces, Sberbank usa su módulo criptográfico para pinpads y lanza una biblioteca especial "parcheada", en la cual, como ya entendimos, el archivo "p" no se crea. Escribimos a Sberbank sobre esto y en unos días obtendremos la respuesta de que "bajo el sistema original, se creará el archivo" p ", pero bajo el parche a Cryptor no lo hará". Les daremos la piedra n. ° 10 en unos días, porque necesita trabajar ahora.
Afortunadamente o desafortunadamente, las funciones que utilizamos para realizar operaciones devuelven la estructura ya mencionada:
struct auth_answer{ int TType; unsigned long Amount; char RCode[3]; char AMessage[16]; int CType; char* Check; };
Oh, bien, el cheque ya está allí, podemos guardarlo en un archivo nosotros mismos o imprimirlo inmediatamente en JSON ...
printf("%s\n", answer.Check);
Y la aplicación se bloquea debido al acceso a un puntero no válido.
CAPÍTULO 14. Fuego y agua.
4 a.m. Realizamos Seth Bandha Sarvangasana para calmarnos y leemos cuidadosamente el manual:
imagen [fuera] del cheque, GlobalFree debe liberarla en el programa de llamadas
¿Qué nos da esto? Mucho En primer lugar, dado que el puntero debe limpiarse con GlobalFree, se solucionó con
GlobalAlloc . Por lo tanto, no produce un puntero a la memoria, como lo fue en la versión de 16 bits, sino un número de objeto con el tipo semántico declarado HGLOBAL, que se puede alimentar en la función GlobalSize para obtener el tamaño del bloque asignado y GlobalLock para bloquear un trozo de memoria, pero para obtener el puntero original. Por cierto, piedra # 6 hacia Microsoft para NIH malloc y gratis, que están en la biblioteca estándar.
printf("%s\n", GlobalLock(answer.Check));
Y aún así obtener una gota. Bien, ¿qué muestra GlobalSize? Cero? De alguna manera extraño.
Verificamos otras funciones que también deberían dar un desliz: vemos la misma imagen.
Solo se me ocurre que puedo generar un recibo por mi cuenta de acuerdo con los datos que puede proporcionar la función de pago más genial (sí, Sberbank tiene funciones llamadas card_authorize2..14, no arrojaré una piedra por ello):
struct auth_answer14 { auth_answer ans; char AuthCode[MAX_AUTHCODE]; char CardID[CARD_ID_LEN]; int ErrorCode; char TransDate[TRANSDATE_LEN]; int TransNumber; int SberOwnCard; char Hash[CARD_HASH_LEN]; char Track3[CARD_TRACK3_LEN]; DWORD RequestID; DWORD Department; char RRN[MAX_REFNUM]; DWORD CurrencyCode; char CardEntryMode; char CardName[MAX_CARD_NAME_LEN]; char AID[MAX_AID_ASCII_LEN]; char FullErrorText[MAX_FULL_ERROR_TEXT]; DWORD GoodsPrice; DWORD GoodsVolume; char GoodsCode[MAX_GOODS_CODE+1]; char GoodsName[MAX_GOODS_NAME]; }; PILOT_NT_API int card_authorize14( char *track2, struct auth_answer14 *auth_answer, struct payment_info_item *payinfo );
Estamos tratando de seleccionar los campos ... Descubrimos que solo una cosa nos separó de la felicidad: Apellido y Nombre del titular de la tarjeta. Sin ellos, un resbalón no se considera legal :Detalles: identificador de un cajero automático, terminal electrónico u otro medio técnico destinado a transacciones con tarjetas de pago; tipo de operación; fecha de transacción; monto de la transacción; moneda de transacción; código de autorización del monto de la comisión; Detalles de la tarjeta de pago.
Es una pena, pero formar un boleto legal con los datos que tenemos no funcionará.Vamos a profundizar en la documentación nuevamente.Encontramos el ejemplo que Sberbank ofrece en el directorio "ejemplos" std::cout << "Authorization completion finished with code '" << result << "'" << std::endl; std::ofstream file(CHEQUE_FILENAME); file << argument.auth_answ.Check; file.close(); if (argument.auth_answ.Check) { std::cout << "Cheque saved to file " << CHEQUE_FILENAME << std::endl;
Simplemente muestra el texto ubicado en el puntero. Pero ya hemos visto que no funciona así ... Por si acaso, recopilaremos su ejemplo y lo ejecutaremos. Salida en la línea `file << argumento.auth_answ.Check;`, bueno, Sberbank, sostenga la piedra # 11 para ejemplos que no funcionan.7 a.m. Ya puede escribir a los desarrolladores de otro contenedor que se escribió en Delphi hace varios años. Recibimos la respuesta de que todo funciona para ellos. Estamos buscando la base de su envoltorio y encontramos en github : TAuthAnswer = packed record TType: integer; Amount: UINT;
Conversión simple de tipo a puntero sin llamadas a funciones.Comenzamos a sospechar de los espíritus malignos.CAPÍTULO 17. Tormenta eléctrica estalló
La gente comienza a regresar a la oficina, asintiendo con simpatía. PO no parece muy feliz de saber las últimas noticias.Aquí recuerdo un detalle. Cuando mostramos los campos de la estructura # 14 para ver sus valores, se cortó un byte de cada línea. Por un lado, por otroAtencionEn la estructura auth_answer14, el nombre del producto es un carácter más corto que en gate.dll TGoodsData. Solucione este error como estándar
Tal vez todavía está conectado con ... Unaterrible suposición aparece en el cerebro como un rayo. Declarar la estructura como typedef struct __attribute__((packed)) { int TType; unsigned long Amount; char RCode[3]; char AMessage[16]; int CType; char* Check; };
Y ...nada cambia.Tamaño fijo = 0, Bloqueo fijo = NULL.DolorDecaimientoInvoluntariamente, busca con los ojos una viga cómoda en el techo, de modo que pueda soportar el peso. Después de tantas horas sin parar de codificar y estudiar la documentación, filas delgadas de bytes flotan ante nuestros ojos. Pero, ¿qué pasa si imprimimos bytes que generalmente se devuelven? u32 i; for (i = 0; i < sizeof(answ); i++) { printf("%02x ", *((u8 *)&answ + i)); } printf("\n"); C:\banks\sber\sb_pilot>sb_pilot.exe 1 1000 01 00 00 00 e8 03 00 00 30 00 00 ce e4 ee e1 f0 e5 ed ee 00 00 00 00 00 00 00 00 02 00 00 00 f8 6c 7a 00 00
`30 00 00 ce` - lo que significa que Sberbank todavía usa estructuras empaquetadas. Pero no hay una palabra sobre esto en los encabezados. Por lo tanto, los ejemplos no funcionan, por lo tanto, es imposible obtener un puntero al texto al final, porque se rompe debido a un cambio de 1 byte. ¡Piedra enorme y espinosa hacia Sberbank!Y entonces un matiz maaaalenky me llamó la atención. 4 + 4 + 3 + 16 + 4 + 4 = 35. Y aquí hay 36 bytes, Obelix.Si hay 36 bytes, entonces el compilador sigue alineando la estructura. Entonces, entre RCode y AMessage, todavía se inserta un byte adicional. Pero por que?
¡Después de todo, indicamos `__packed__`!CAPÍTULO 18. El viaje de regreso
Las razones por las que la alineación todavía está activa aparecieron en 2012: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=52991 . Solo se corrigió un error en GCC 8 (¡una piedra para 6 años de errores!), Que aún no se puede actualizar. Afortunadamente, hay una solución alternativa: -mno-ms-bitfields
Ahora no analizaremos el mecanismo de operación de esta bandera, solo pasala al compilador:
¡Slip! Querida! Te extrañé, ni siquiera juraré por el krakozyabry, ya le tiré una piedra.Y finalmente, le damos a Microsoft una piedra, porque GlobalSize / Lock da ceros a punteros inválidos.CAPÍTULO 19. Último capítulo
Para minimizar el número de ifdefs para la capa sb_pilot, escribimos una aplicación separada que imita completamente la versión de linux de sb_pilot. Por lo tanto, dejando el código de capa n. ° 1 igual, dejando solo una condición: #if defined(BXI_OS_GLX) #define GFJ_PILOT_EXECUTABLE "./sb_pilot" #elif defined(BXI_OS_WIN) #define GFJ_PILOT_EXECUTABLE "./sb_pilot.exe" #endif
Resultados de la batalla:- Sberbank: 12 piedras
- Microsoft: 7 piedras
- CCG: 1 piedra
Logro-recolección en nuestro tablero de comando: