Как перевести приведение адреса памяти на C в вызов python ctypes?

#python #ctypes #freetds

#python #ctypes #freetds

Вопрос:

Извините, если название неточное, я не уверен на 100%, что оно правильно описывает ситуацию:

Я пытаюсь взаимодействовать с C-библиотекой FreeTDS, используя модуль ctypes Python. У меня есть некоторый код, работающий на удивление хорошо, но я столкнулся с одной проблемой. Я не уверен, как перевести последний параметр вызова dbbind() ниже в ctypes.

Пример C, который я привожу, является:

   /* these variable types are defined in the FreeTDS library */
  DBINT customer_id;
  DBCHAR company_name[255];
  DBFLT8 avg_income;

  /* <snip> */


  /* Now bind the returned columns to the variables */
  /* BYTE is defined in the FreeTDS library */
  dbbind(dbconn, 1, INTBIND, 0, (BYTE *)amp;customer_id);
  dbbind(dbconn, 2, NTBSTRINGBIND, 0, (BYTE *)amp;company_name);
  dbbind(dbconn, 3, FLT8BIND, 0, (BYTE*)amp;avg_income);
  

Итак, А) как мне определить мои переменные в Python как типы переменных из библиотеки и Б) как мне перевести «(BYTE *)amp;company_name» и т.д. В вызовы ctypes?

Спасибо!

Решение: благодаря Zuljin я смог разработать следующее:

 import ctypes as ct

#<snip>

cid = ct.c_int()
cname = ct.create_string_buffer(256)
cavgincome = ct.c_float()
dtlib.dbbind(cdbconn, 1, INTBIND, 0, ct.byref(cid))
dtlib.dbbind(cdbconn, 2, NTBSTRINGBIND, 0, cname)
dtlib.dbbind(cdbconn, 3, REALBIND, 0, ct.byref(cavgincome))

while dtlib.dbnextrow(cdbconn) != NO_MORE_ROWS:
    print '%s | %s | %s' % (cid, cname.value, cavgincome)
  

Ответ №1:

Я думаю, вам следует просто проверить, что стоит за этими типами DBINT, DBCHAR, DBFLT8. Вероятно, это int, char и double. И для этих базовых типов вы можете найти ctypes — вероятно, c_int, c_char, c_double. Итак, теперь вы можете создавать экземпляры python, которые будут содержать значения, возвращаемые функцией. Чтобы передать эти значения в качестве параметра указателя, вам нужно использовать byref() функцию. Что-то вроде этого:

 customer_id = c_int()
dbbind(dbconn, 1, INTBIND, 0, byref(customer_id))
  

РЕДАКТИРОВАТЬ: Для имени вы должны создать пустой символьный буфер. Для этого ctypes предоставляет 2 функции create_string_buffer и create_unicode_buffer. Выходной объект из этих функций может быть напрямую передан вашей функции. Вот пример (на Python 3) обычного вызова функции scanf в юникоде в Windows.

 from ctypes import *
libc = cdll.msvcrt

buf = create_string_buffer(256) 
buf_u = create_unicode_buffer(256)

libc.scanf(b"%s",  buf)
libc.wscanf("%s",  buf_u)

print(buf.value)
print(buf_u.value)
  

Комментарии:

1. Спасибо за предложение. Так вы думаете, мне не нужно что-либо делать с приведением байтов?

2. Char255 = c_char * 255 имя_компании = Char255() dbbind(dbconn, 1, VARYCHARBIND, 0, byref(имя_компании)) Вышесказанное приводит к ошибке seg.

3. почему 255? Ваш массив имеет длину 256?

4. ваши предложения в сочетании с изменением констант bind привели меня в нужное место. Спасибо!