1

Тема: AHK: DllCall и правильный вызов функций.

Добрый день, форумчане. Работаю АСУшником. Программирую контроллеры SIEMENS. Наткнулся на библиотеку snap7.dll которая подключается и читает данные в DB контроллера, состояние контроллера и т.д. Только вот как правильно вызывать эту библиотеку через DllCall я так и не понял. Я даже нашел на Гитхабе чью то функцию с примером работы данной дллки. Но все так же не могу понять некоторые моменты данной программы. Как же дописать нужные вызовы DllCall в функции? Помогите хотя бы разобраться в этих скриптах с вызовами.
S7.ahk

+ открыть спойлер

g_lastError := 0

; S7Area
S7AreaPE := 0x81 	;Process Inputs.
S7AreaPA := 0x82 	;Process Outputs.
S7AreaMK := 0x83 	;Merkers.
S7AreaDB := 0x84 	;DB.
S7AreaCT := 0x1C 	;Counters.
S7AreaTM := 0x1D 	;Timers

; S7WordLength
S7WordLengthBit 	:= 0x01
S7WordLengthByte 	:= 0x02
S7WordLengthWord 	:= 0x04
S7WordLengthDword 	:= 0x06
S7WordLengthReal 	:= 0x08
S7WordLengthCounter := 0x1C
S7WordLengthTimer 	:= 0x1D

; Initialisierung
hModule := DllCall("LoadLibrary", Str, PathCombine(A_ScriptDir, "snap7.dll"))
if(hModule == -1 || hModule == 0) {
	MsgBox, 48, Error, Библиотека snap7.dll не найдена
	ExitApp
}

S7_Create_func := DllCall("GetProcAddress", "UInt", hModule, "Str", "Cli_Create")
S7_Create() {
	global S7_Create_func
	return DllCall(S7_Create_func)
}

S7_ConnectTo_func := DllCall("GetProcAddress", "UInt", hModule, "Str", "Cli_ConnectTo")
S7_ConnectTo(obj, ip, rack, slot) {
	global S7_ConnectTo_func
	return execute(DllCall(S7_ConnectTo_func, "uint", obj, "str", ip, "int", rack, "int", slot))
}

S7_ReadArea_func := DllCall("GetProcAddress", "UInt", hModule, "Str", "Cli_ReadArea")
S7_ReadBit(obj, area, db, offset, bitOffset, bit) {
	global S7_ReadArea_func, S7WordLengthBit
	
	buf := bit ;что это такое?
	bufPtr := &buf
	
	MsgBox, %bufPtr%
	return execute(DllCall(S7_ReadArea_func, "UInt", obj, "Int", area, "Int", db, "Int", offset * 8 + bitOffset, "Int", 1, "Int", S7WordLengthBit, "Ptr", &bufPtr))
}
/*
S7_DBRead_func := DllCall("GetProcAddress", "UInt", hModule, "Str", "Cli_DBRead")
S7_DBRead(obj, db, start, size) {
	global S7_DVRead_func, S7WordLengtByte
	
	buf := byte
	bufPtr := &buf
	
	MsgBox %bufPtr%
	return execute(dllcall(S7_DBRead_func, "Uint", obj, "Int", db, "Int", start, "Int", size, "Ptr", &bufPtr))
}
*/
S7_WriteArea_func := DllCall("GetProcAddress", "UInt", hModule, "Str", "Cli_WriteArea")
S7_WriteBit(obj, area, db, offset, bitOffset, bit) {
	global S7_WriteArea_func, S7WordLengthBit
	
	buf := bit
	bufPtr := &buf
	
	MsgBox, %bufPtr%
	return execute(DllCall(S7_WriteArea_func, "UInt", obj, "Int", area, "Int", db, "Int", offset * 8 + bitOffset, "Int", 1, "Int", S7WordLengthBit, "Ptr", &bufPtr))
}
;а эти функции вообще не используются что ли?
PathCombine(abs, rel) {
    VarSetCapacity(dest, (A_IsUnicode ? 2 : 1) * 260, 1) ; MAX_PATH
    DllCall("Shlwapi.dll\PathCombine", "UInt", &dest, "UInt", &abs, "UInt", &rel)
    Return, dest
}
;а эти функции вообще не используются что ли?
BEint(ByRef Var, ByRef BE, Bytes) {
	VarSetCapacity(BE, Bytes, 0)
	
	loop, %Bytes%
	{
		byte := NumGet(Var, Bytes-A_Index, "UChar")
		NumPut(byte, BE, A_Index-1, "UChar")
		
	}
	
	loop, %Bytes% {
		MsgBox, % NumGet(BE, A_index - 1, "UChar")
	}
}

execute(retn) {
	global g_lastError
	if(retn) {
		g_lastError = retn
	}
	
	return (retn == 0)
}

S7_GetLastError() {
	global g_lastError
	err := g_lastError
	g_lastError := 0
	return err
}

example.ahk

+ открыть спойлер

#SingleInstance, Force
#Include S7.ahk

obj := S7_Create()
ret := S7_ConnectTo(obj, "192.168.1.12", 0, 3) ;(Объект соединения,IP адрес, Рэк, Слот)
MsgBox %ret%

ret := S7_ReadBit(obj, 0x83, 0, 0, 1, 1)	;(obj, area, db, offset, bitOffset, bit) 
error := S7_GetLastError()
MsgBox %ret% `n%error%

;~ result := S7_DBRead(obj, 14, 0, 20)
;~ MsgBox %result%
return
Post's attachments

snap7.dll 217 kb, 7 downloads since 2017-08-02 

You don't have the permssions to download the attachments of this post.

2

Re: AHK: DllCall и правильный вызов функций.

Может с документацией кто то сможет помочь?
https://github.com/SCADACS/snap7/blob/m … refman.pdf

3

Re: AHK: DllCall и правильный вызов функций.

Raven, чтобы проверить эту библиотеку нужны эти самые контроллеры, только где их взять? Не думаю, что у кого-то они есть.

4

Re: AHK: DllCall и правильный вызов функций.

Не не не... Мне нужно просто объяснить как правильно вызывать данные функции через DllCall. И чужие примеры я привел исключительно для того что бы мне помогли разобраться хотя бы в них. Все остальное я сам проверю и уведомлю форум о результатах.

5

Re: AHK: DllCall и правильный вызов функций.

Форумчане, ответе на такой вопрос - почему в коде встречается значок & ?

buf := bit
bufPtr := &buf
return execute(DllCall(S7_ReadArea_func, "UInt", obj,
										 "Int", area,
										 "Int", db,
										 "Int", offset * 8 + bitOffset,
										 "Int", 1,
										 "Int", S7WordLengthBit,
										 "Ptr", &bufPtr)) ;Почему  &bufPtr??? 

Это указатель? Или что? Для чего его туда впихнули то? Почему не обычная переменная?

6 (изменено: stealzy, 2017-08-04 17:37:04)

Re: AHK: DllCall и правильный вызов функций.

Ptr - pointer - указатель. Почему не переменная вопрос к авторам библиотеки, почему ф-ия требует указатель параметром.

7

Re: AHK: DllCall и правильный вызов функций.

Ладно, немного разобрался с вызовом функций. Примерно так.

ReadDB(obj, db, start, size){
	VarSetCapacity(data, 10240000, 0)
	DllCall("snap7.dll\Cli_DBRead", "UInt", obj, "Int", db, "int", start, "int", size, "str", data)
	return data
}

Возник другой вопрос, как правильно получать данные? По идее "data" это строка чисел из DB контроллера. А я не получаю ничего из функции если не ставлю "str". Подскажите как мне получить либо массив данных в "data", либо как из строк вида:

B[®дBTУUBeЄСBB)ЋB{ЗЋAшФ
B[®дBTд
B[К«BTНЗBeµ/BB:9B{Т«Aшк9BG)/
B[ЈЗBTНЗBeµ/BB:9B{јrAшк9BG%{
B[ЈЗBTИ9BeЄСBB:9B{¶дAшк9BG)/
B[©UBTУUBe rBB:9B{¶дAшЅЗBG)/
B[ЈЗBTУUBe rBB:9B{¶дAшЅЗBG%{

получить биты данных. То есть преобразовать в другой формат данных.
Да и вообще, чего то я на форуме не нашел преобразования типов данных из оного в другое. Например из string в ineger, из integer в binary, из binary в string ну и так далее. Не подскажите функции преобразования типов?

8 (изменено: stealzy, 2017-08-08 16:28:19)

Re: AHK: DllCall и правильный вызов функций.

Raven пишет:

на форуме не нашел преобразования типов данных из оного в другое

AutoHotkey - язык с динамической неявной слабой типизацией, поэтому внутри самого языка такой задачи быть не может.
Про ф-ии, превращающие юникод/ansi строки в строки вида "011010001001111010", можно не упоминать.

Raven пишет:

"data" это строка чисел из DB контроллера. А я не получаю ничего из функции если не ставлю "str"

DllCall функции возвращают фиксированный тип, права выбирать тип самому вам никто не давал.
В данном случае видимо некая проблема с кодировкой.
В VarSetCapacity ноль не лишний приписан?
Пример строки, которая должна возвращаться, есть?

9 (изменено: ypppu, 2017-12-21 19:02:25)

Re: AHK: DllCall и правильный вызов функций.

stealzy пишет:

Ptr - pointer - указатель. Почему не переменная вопрос к авторам библиотеки, почему ф-ия требует указатель параметром.

Посылать в фунцию указатель на переменную - выгодно. Если посудить самому, когда в функцию посылаешь значительные данные(от 300 мб - к примеру) то в функции выделяется память для этой переменной(т.е. 300 мб). А зачем еще 300 мб выделять? Если можно выделить 8 байт, в которых будет указатель на эти 300 мб.
В итоге: при работе с указателем - 300,8 мб. При работе напрямую - 600 мб.

10 (изменено: Raven, 2017-08-09 16:12:42)

Re: AHK: DllCall и правильный вызов функций.

Тогда такой вопрос. Вот выдержка из документации по DLL:


int Cli_DBRead(S7Object Client, int DBNumber, int Start, int Size, 
    void *pUsrData); 
 
function Cli_DBRead(Client : S7Object; DBNumber, Start,  
    Size : integer; pUsrData : pointer) : integer; 
+ открыть спойлер
  • Client - Native Integer - The handle as return value of Cli_Create(), passed by value.

  • DBNumber - integer - DB Index (0..0xFFFF).

  • Start - integer - Offset to start

  • Size - integer - Size to read (bytes)

  • pUsrData - Pointer to memory area - Pointer user buffer.

Это мой код:

ret := ReadDB(obj, 110, 8, 4)
Msgbox %ret%
ReadDB(obj, db, start, size){
	;global obj
	VarSetCapacity(data, 10240000, 0)
	DllCall("snap7.dll\Cli_DBRead", "UInt", obj, "Int", db, "int", start, "int", size, "str", data)
	return data
}

Правильно ли я в своем коде использую  "str", data , если в документации сказано "pUsrData - Pointer to memory area - Pointer user buffer" ??? Если нет, то как будет правильно?

11

Re: AHK: DllCall и правильный вызов функций.


"ptr", &data)

"ptr" интерпретируется, как "uint" или "int64" в зависимости от разрядности (32/64) интерпретатора АНК, выполняющего код.

12

Re: AHK: DllCall и правильный вызов функций.

Raven

pUsrData - Pointer to memory area - Pointer user buffer

Pointer - указатель.

13

Re: AHK: DllCall и правильный вызов функций.

Охренеть, в документации написан указатель, а вы переменную пихаете.
Вызывайте так тогда, чего уж:

DllCall("snap7.dll\Cli_DBRead", "сделатьмнехорошо", x)

14 (изменено: Raven, 2017-08-09 16:43:41)

Re: AHK: DllCall и правильный вызов функций.

stealzy Ну подскажите как нужно правильно запихнуть туда указатель. То я его как только дуда не пихал... Я так понимаю что нужно сначала объявить переменную а потом уже указатель на нее, поместить в функцию. Только как это сделать то?

15

Re: AHK: DllCall и правильный вызов функций.

YMP вам уже написал. Как вас вообще к контроллерам не боятся пускать ;-)?

16

Re: AHK: DllCall и правильный вызов функций.

В контроллерах проще все. :-) мне вот такой код легче читать чем разбираться в указателях DllCall Autohotkey'я :-)

+ открыть спойлер
 
      L     DB110.DBD  352
      L     9.500000e+001
      >R    
      S     M     90.0

      AN    I     16.0                
      FR    T     58
      L     S5T#5S                     
      SD    T     58

      A     T     58
      S     M     90.0

Туплю я че то с этими dllcall... Вот не получается у меня выковырнуть данные из этих указателей...

17

Re: AHK: DllCall и правильный вызов функций.

А разве строка не передаётся в DllCall указателем и следующие вызовы не делают одно и то же?

DllCall("MyFunc", "Str", "Hello!")

s := "Hello"
DllCall("MyFunc", "Str", s)

DllCall("MyFunc", "Ptr", &s)

DllCall("MyFunc", "Ptr", &(s := "Hello!"))

18 (изменено: ypppu, 2017-12-21 19:02:48)

Re: AHK: DllCall и правильный вызов функций.

wisgest
В AHK - нет. По крайне мере у меня так не выходит) А в c++ может быть и работает.

19 (изменено: teadrinker, 2017-08-10 02:08:27)

Re: AHK: DllCall и правильный вызов функций.

Это легко проверить:

str := "привет"
DllCall("CharUpper", Str, str)
MsgBox, % str

str := "привет"
DllCall("CharUpper", Ptr, &str)
MsgBox, % str
Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

20

Re: AHK: DllCall и правильный вызов функций.

teadrinker, у меня оба раза

---------------------------
test.ahk
---------------------------
ПРИВЕТ
---------------------------
ОК   
---------------------------

21

Re: AHK: DllCall и правильный вызов функций.

У меня тоже.
А ещё нагляднее так:

addr := RegisterCallback("MyFunc")
DllCall(addr, Str, "привет")

MyFunc(pStr)  {
   MsgBox, % pStr . "`n" . StrGet(pStr)
}
Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

22

Re: AHK: DllCall и правильный вызов функций.

Передавать буфер, как строку, не стоит по той же причине, по которой не стоит писать "тоже" вместо "то же" и наоборот. Хотя произносится и одинаково, но разное написание облегчает восприятие текста. Мозгу не приходится выводить смысл из контекста.

По той же причине мне не нравятся вещи вроде


, Str, str)

23

Re: AHK: DllCall и правильный вызов функций.

Про «"тоже" вместо "то же"» не понял. Разве у меня неправильно?

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

24

Re: AHK: DllCall и правильный вызов функций.

Нет, это я просто как пример привёл.

25

Re: AHK: DllCall и правильный вызов функций.

YMP пишет:

мне не нравятся вещи вроде

, Str, str)

Мне тоже. А вот так нравится

, "Str", str)

26

Re: AHK: DllCall и правильный вызов функций.

wisgest пишет:
YMP пишет:

мне не нравятся вещи вроде

, Str, str)

Мне тоже. А вот так нравится

, "Str", str)

Какие все придирчивые.

27

Re: AHK: DllCall и правильный вызов функций.

MandarinKa02, это точно.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

28 (изменено: Alectric, 2017-08-10 17:58:04)

Re: AHK: DllCall и правильный вызов функций.

Raven
Тоже работаю с сиеменсом, новичок в STL. К сожалению нет доступа к IP модулям чтобы "поиграться" со скриптами. Будет возможность обязательно попробую.

Raven пишет:

;а эти функции вообще не используются что ли?
PathCombine(abs, rel) {

Используется в начале при инициализации.

Raven пишет:

;а эти функции вообще не используются что ли?
BEint(ByRef Var, ByRef BE, Bytes) {
VarSetCapacity(BE, Bytes, 0)

loop, %Bytes%
{
byte := NumGet(Var, Bytes-A_Index, "UChar")
NumPut(byte, BE, A_Index-1, "UChar")

}
loop, %Bytes% {
MsgBox, % NumGet(BE, A_index - 1, "UChar")
}
}

Как я понял, BEint переворачивает данные (первый байт в конец, последний в начало) и выводит по одному байту в сообщении.
Берёт из переменной Var (может содержать сколько задашь байт) байт с конца и кладёт в переменную BE в начало, перебирая по одному байту. Видно что функция недоделанная, скорей всего использовалась для отладки.

Raven пишет:

buf := bit ;что это такое?
bufPtr := &buf

buf - номер читаемого бита, как я понял, или количество бит? Зачем там тогда area (область), значит читается какая-то область.


Функция S7_DBRead на мой взгляд практичнее - указываешь номер ДБ, откуда начать и сколько считать. Правда она тоже выглядит недописанной.

Можно увидеть твой текущий код?

Win 10 x64
AHK v1.1.33.02
                       Справка тебе в помощь.

29 (изменено: Alectric, 2017-08-10 18:18:20)

Re: AHK: DllCall и правильный вызов функций.

Всё сообразил что за area.

; S7Area
S7AreaPE := 0x81 	;Process Inputs.
S7AreaPA := 0x82 	;Process Outputs.
S7AreaMK := 0x83 	;Merkers.
S7AreaDB := 0x84 	;DB.
S7AreaCT := 0x1C 	;Counters.
S7AreaTM := 0x1D 	;Timers

Область памяти в ПЛК.

Win 10 x64
AHK v1.1.33.02
                       Справка тебе в помощь.

30

Re: AHK: DllCall и правильный вызов функций.

Alectric Дык я ж эту функцию сам накорябал и пытаю ее. S7_DBRead как раз таки и требуется мне. С ней проще всего экспериментировать. Да и в контроллер лишнего не запишешь.

ret := ReadDB(obj, 110, 8, 4)
Msgbox %ret%
ReadDB(obj, db, start, size){
	VarSetCapacity(data, size, 0)
	DllCall("snap7.dll\Cli_DBRead", "UInt", obj, "Int", db, "int", start, "int", size, "str", data)
	return data
}

Я получаю с этой функции строки вида "Bkдд" "Bk{U" "BkuЗ" (4 байта (real значение в контроллере)). Я так понимаю сырые биты информации преобразуются в строку. По другому у меня никак не получилось получить данные... Не догоняю видимо я...
И вот что  чувак написал мне на гитхабе у которого я стырил изначальный код.

ReadDB returns binary data encoded in a string. Use NumGet to cast it to an integer. Also consider the Endianness. According to the official SNAP7 documentation, S7 uses Big Endian whereas "normal" computers use Little Endian. You will have to convert it manually in AHK

То есть как раз функция BEint и нужна для переворачивания байт так как в контроллерах сиеменс используется порядок байт "Big Endian".
Вообщем еще копать и копать. А на работе еще и работать иногда приходится ведь!

31 (изменено: Alectric, 2017-08-11 13:43:34)

Re: AHK: DllCall и правильный вызов функций.

Похоже на то, как я возился с КОМпортом. http://forum.script-coding.com/viewtopic.php?id=12659
Нужно воспринимать полученные данные не как текст, а как бинарные данные (он тебе так и ответил) и использовать NumGet для перевода данных в формат понятный для ahk.

 
      ; заполняем массив byte
        loop,% RS232_Bytes_Received*2
        {
          if (a_index&0x1)
          {
            bytenum:=(a_index-1)//2 ; номер байта

--------------> ; сам байт состоит из 2х символов HEX

            byte%bytenum%:=chr(numget(data,2*(a_index-1),"uchar")) . chr(numget(data,2*(a_index-1)+2,"uchar"))
          }
        }

     b7:="0x" byte7                    ; 1 байт
     i0:="0x" byte4 byte3         ; 2 байта
     w0:="0x" byte11 byte10 byte9 byte8 ; 4 байта

Зря с реала начал, начни лучше со слов или интегеров.

Полученные байты позднее можно будет разложить на нужные биты.

Raven пишет:

p7.dll\Cli_DBRead", "Ptr", obj,

Тут "Ptr" нужен.

RS232_Read(RS232_FileHandle,Data_Length,ByRef RS232_Bytes_Received)
{
  Num_Bytes:=Data_Length+Data_Length//3
  SetFormat,Integer,HEX
  VarSetCapacity(Data,Num_Bytes,0xff)
  if RS232_FileHandle
  Read_Result := DllCall("ReadFile"
       ,"ptr" , RS232_FileHandle   ; hFile
       ,"ptr"  , &Data             ; lpBuffer
       ,"Int"  , Num_Bytes        ; nNumberOfBytesToRead
       ,"ptr*", RS232_Bytes_Received   ; lpNumberOfBytesReceived
       ,"Int"  , 0)               ; lpOverlapped
  If (Read_Result != 1)
  {
    RS232_Close(RS232_FileHandle)
    COMFail=1
    return
  }
  Loop,% RS232_Bytes_Received
  {
    t:=NumGet(Data,a_index-1,"UChar")
    StringTrimLeft,t,t,2
    If (StrLen(t)=1)
      t:="0" t
    Data_HEX.=t
  }
  SetFormat,Integer,DEC
  Return Data_HEX
}

Переделал:

ReadDB(obj, db, start, size){
	VarSetCapacity(data, size, 0)
    SetFormat,Integer,HEX
	DllCall("snap7.dll\Cli_DBRead", "Ptr", obj, "Int", db, "int", start, "int", size, "ptr", &data)
    Loop,% size
    {
      t:=NumGet(Data,a_index-1,"UChar")
      StringTrimLeft,t,t,2
      If (StrLen(t)=1)
        t:="0" t
      Data_HEX.=t
    }
    SetFormat,Integer,DEC
	return Data_HEX
}

Есть вероятность что я где-то ошибся, возможно для функции библиотеки нужно все таки str:

ReadDB(obj, db, start, size){
	VarSetCapacity(data, size, 0)
    SetFormat,Integer,HEX
	DllCall("snap7.dll\Cli_DBRead", "Ptr", obj, "Int", db, "int", start, "int", size, "str", data)
    Loop,% size
    {
      t:=NumGet(Data,a_index-1,"UChar")
      StringTrimLeft,t,t,2
      If (StrLen(t)=1)
        t:="0" t
      Data_HEX.=t
    }
    SetFormat,Integer,DEC
	return Data_HEX
}
Raven пишет:

А на работе еще и работать иногда приходится ведь!

И не говори.

Win 10 x64
AHK v1.1.33.02
                       Справка тебе в помощь.

32

Re: AHK: DllCall и правильный вызов функций.

Alectric Поздравляю коллега! у вас получилось то что не получалось у меня вот уже более недели
Обе функции возвращают одно и то же число. И то что я вижу в контроллере (DW#16#426AA2AB) ничем не отличается от получаемых данных(426AA2AB). Осталось придумать функции преобразования данных и можно писать свою скаду!

33 (изменено: Alectric, 2017-08-11 14:01:31)

Re: AHK: DllCall и правильный вызов функций.

Raven пишет:

свою скаду

Тоже мечтаю об этом, это же ужасно гибкие и бесплатные интерфейсы можно писать, работающие через эзернет!

Raven пишет:

  у вас получилось то что не получалось у меня вот уже более недели

Знал бы ты сколько я с КОМпортом возился.

Вот кстати про биты тему нашел, чтобы биты из байтов "вытягивать".
Как слово в реал преобразовать ума не приложу, голова кипит.

PS Тему наверно лучше переименовать: AHK: Siemens S7 через Ethernet в AHK.

Win 10 x64
AHK v1.1.33.02
                       Справка тебе в помощь.

34 (изменено: Malcev, 2018-12-28 21:35:11)

Re: AHK: DllCall и правильный вызов функций.

У меня не вызывается следующая функция:
https://docs.opencv.org/2.4/modules/cor … reateimage

CreateImage

Creates an image header and allocates the image data.

C: IplImage* cvCreateImage(CvSize size, int depth, int channels)

Python: cv.CreateImage(size, depth, channels) → image
    Parameters:

        size – Image width and height
        depth – Bit depth of image elements. See IplImage for valid depths.
        channels – Number of channels per pixel. See IplImage for details. This function only creates images with interleaved channels.

Структура CvSize:

int width
int height

https://docs.opencv.org/ref/2.4/da/dcb/ … vSize.html

VarSetCapacity(cvsize, 8, 0)
NumPut(100, cvsize, 0, "int")
NumPut(100, cvsize, 4, "int")
msgbox % DllCall("opencv_core2413.dll\cvCreateImage", "ptr", &cvsize, "int", IPL_DEPTH_8U := 8, "int", channels := 2, "Cdecl Ptr")
msgbox % a_LastError

Как правильно ее нужно вызывать?
Dll 32-bit:
https://ru.files.fm/u/6ny65dcr

35

Re: AHK: DllCall и правильный вызов функций.

На autoit же вызывается:

Local $cvsize = DllStructCreate("int;int")
DllStructSetData($cvsize, 1, 100)
DllStructSetData($cvsize, 2, 100)
Local $Result = DllCall("opencv_core2413.dll", "ptr:cdecl", "cvCreateImage" , "struct" , $cvsize , "int" , 8, "int" , 2)
MsgBox(1,1, $Result[0])

Баг автохотки?

36 (изменено: YMP, 2018-12-29 09:25:31)

Re: AHK: DllCall и правильный вызов функций.

Судя по всему, там передаётся сама структура, а не указатель на неё. Можно использовать int64.


width := 100
height := 100
cvsize := (height << 32) | width
msgbox % DllCall("opencv_core2413.dll\cvCreateImage", "int64", cvsize, "int", IPL_DEPTH_8U := 8, "int", channels := 2, "Cdecl Ptr")
msgbox % a_LastError

37

Re: AHK: DllCall и правильный вызов функций.

Спасибо!
Не слышал о таком способе передачи структуры.
Гдве вы о нем узнали?

38

Re: AHK: DllCall и правильный вызов функций.

Уже и не помню. Обычно структуры по ссылке передаются, т.е. указателем. Не знаю, почему здесь решили передавать копию структуры, в чём тут выгода. Можно ещё разбить на два отдельных параметра.


width := 100
height := 100
msgbox % DllCall("opencv_core2413.dll\cvCreateImage", "int", width, "int", height, "int", IPL_DEPTH_8U := 8, "int", channels := 2, "Cdecl Ptr")
msgbox % a_LastError

Это работает, т.к. каждый простой параметр занимает в стеке 32 бита, так что эти числа будут лежать там рядом, как и в структуре. Но если бы функция была 64-битная, то там и простые параметры занимают 64 бита и уже бы этот способ не сработал для этой структуры.

39

Re: AHK: DllCall и правильный вызов функций.

YMP пишет:

передаётся сама структура

Как я понял, передается содержимое структуры?
А как быть, если структура достигает, к примеру 128,192 байта? Как ее содержимое передать? Или такое только в сказках?
P.S. В целях саморазвития.

40 (изменено: Malcev, 2018-12-29 17:18:47)

Re: AHK: DllCall и правильный вызов функций.

YMP, а если мы имеем структуру 4 элемента "Double".

double  d0,
double  d1 = 0,
double  d2 = 0,
double  d3 = 0

Как отдельные параметры они вроде передаются без ошибки, хотя вроде как 64 бита:

DllCall("opencv_core2413.dll\cvSet", "ptr", pimg, "Double", 255, "Double", 255, "Double", 255, "Double", 255, "ptr", 0, "Cdecl") 

Это вообще правильный и единственный вариант передачи структуры в данном случае, когда ссылка на структуру не передается?
И если у автоит передается всё стандартным объявлением структуры, а у автохотки нет, то, получается, это недочёт автохотки?

41

Re: AHK: DllCall и правильный вызов функций.

Malcev пишет:

Как отдельные параметры они вроде передаются без ошибки, хотя вроде как 64 бита:

В смысле double занимает 64 бита? Да, верно. Я имел в виду, что если функция 64-битная, а вы передаёте структуру из 32-битных целых, то при передаче отдельными аргументами каждое займёт в стеке 64 бита, т.е. они уже будут в памяти лежать не вплотную. Получится структура из 64-битных целых.

Т.е. если аргумент меньше 32 бит (для x86) или 64 бит (для x64), то под него всё равно выделяется 32 и 64 бита соответственно. А если он больше, то выделяется столько 32- или 64-битных ячеек, чтобы он влез.

Malcev пишет:

Это вообще правильный и единственный ли вариант передачи структуры в данном случае (как ссылка структура не передается)?

Да, правильный. Вероятно, единственный.

42

Re: AHK: DllCall и правильный вызов функций.

MandarinKa02 пишет:

А как быть, если структура достигает, к примеру 128,192 байта? Как ее содержимое передать? Или такое только в сказках?

Упаковывать по частям в те же int64 и передавать эти части как отдельные параметры. Бывает ли такое, не знаю. По идее, это неэкономично.

Тут, возможно, может помешать малопонятная мне неспособность АНК работать с беззнаковыми 64-битными целыми, т.е. uint64. Если при упаковке получится именно такое число, то не будет ли оно искажено впоследствии. Т.е. если в результате получится число 0x8000000000000000 или больше.

И если члены структуры разного типа, а именно целые и дробные, то тут не очень понятно, как их рассовать по int64.

43

Re: AHK: DllCall и правильный вызов функций.

YMP, понял. Благодарю за ответ.

44

Re: AHK: DllCall и правильный вызов функций.

Отписал в баг репорт, посмотрим, что ответят.

45 (изменено: YMP, 2018-12-29 17:56:34)

Re: AHK: DllCall и правильный вызов функций.

Но это, строго говоря, не баг, а отсутствие такой фичи (передачи самой структуры, а не указателя на неё). Это скорее в Wish List.

46

Re: AHK: DllCall и правильный вызов функций.

А автоит передает разве не указатель? Просто в нем при создании структуры нигде не надо указывать, что нужно передавать. Или он сам решает, что передавать?

47

Re: AHK: DllCall и правильный вызов функций.

Передавать нужно то, что ожидает функция. AutoIt передаёт саму структуру, если указан тип "struct", и ссылку если "struct*".

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

48

Re: AHK: DllCall и правильный вызов функций.

Точнее, не саму структуру, а копию. Т.е. передача по значению vs. передача по ссылке (указателем).

49

Re: AHK: DllCall и правильный вызов функций.

А где в функции обозначено, что ожидается передача копии структуры, а не указатель?

50 (изменено: YMP, 2018-12-29 19:40:20)

Re: AHK: DllCall и правильный вызов функций.

Там указан тип CvSize, а это структура. При указателе было бы CvSize *.

Хотя, конечно, могут и указатель на какой-то тип обозвать каким-то другим именем, тогда звёздочки не будет. Но какой-то намёк тогда в имени должен быть, вроде P или ptr. В общем, надо смотреть определение типа в документации.

51 (изменено: Malcev, 2018-12-30 02:53:57)

Re: AHK: DllCall и правильный вызов функций.

Что-то я не понял ответа с оф.форума.

In c (cdecl), as far as I know, passing a struct by value is done by copying it on to the stack, including padding. Your only option to put something on the stack, is by passing parameters via dllcall. I'd expect stdcall behaves the same.
For x64 calling convention, refer to MSDN,
https://docs.microsoft.com/en-us/cpp/bu … ew=vs-2017
   

Structs and unions of size 8, 16, 32, or 64 bits, and __m64 types, are passed as if they were integers of the same size. Structs or unions of other sizes are passed as a pointer to memory allocated by the caller.

So indeed, struct S {int;int;} must be passed as int64 for x64 calling convention, and it can be for cdecl too. But struct S2 {char c;short s;} would need to be passed as "ptr", &S2 for x64. But, for cdecl, S2 would be passed as "int", c | (s << 16), you cannot do "char", c, "short", s. Finally struct d {double; double; ... double;} must be passed as "ptr", &d for x64 and "double", d1, ..., "double", dn for cdecl.
https://www.autohotkey.com/boards/viewt … 59#p255359

Почему для такой структуры {char c;short s;} нельзя передать так - "char", c, "short", s , а {double; double; double;} можно "double", d1, ..., "double", dn for cdecl?
Еще написали, что при такой передачи width и height могут смешаться:

msgbox % DllCall("opencv_core2413.dll\cvCreateImage", "int", width, "int", height, "int", IPL_DEPTH_8U := 8, "int", channels := 2, "Cdecl Ptr")

Действительно могут?

52

Re: AHK: DllCall и правильный вызов функций.

Malcev пишет:

Еще написали, что при такой передачи width и height могут смешаться:

Нет, он имел в виду порядок width и height. Но они так и должны быть. Правила передачи структур, о которых тут говорится, — это рекомендации для разработчиков. Мы имеем дело с уже готовыми функциями и должны передавать так, как они этого ожидают.

В x64 есть особенности, которые нужно учитывать. Элементы структуры выравниваются по "границе типа". Каждый элемент должен располагаться либо в начале структуры, либо по смещению от начала структуры, кратному его размеру. Если имеем структуру {char, short}, то второй элемент не может быть впритык к первому, т.к. имеет размер 2 байта и, значит, должен быть, как минимум, на смещении 2. Т.е. после char делается отступ в 1 байт, чтобы выравнять short.

Кроме того, размер структуры должен быть кратным самому большому её элементу. В структуре {short, char} к концу нужно добавить 1 байт, чтобы размер был не 3, а 4 байта, кратно short.

Адрес структуры в памяти должен быть тоже кратен размеру самого большого элемента. Но это больше рекомендация, я так понимаю. Если внутри структура выравнена, то проблем быть не должно. Хотя смутно помню какие-то ситуации, где выравнивание данных именно в памяти было критично. Так что если какие-то непонятные глюки, то можно проверить и это условие.

Malcev пишет:

Почему для такой структуры {char c;short s;} нельзя передать так - "char", c, "short", s , а {double; double; double;} можно "double", d1, ..., "double", dn for cdecl?

Потому что char и short меньше, чем ячейка стека (32 бита), а выделено каждому будет по ячейке, как я уже выше писал. Т.е. в итоге в памяти получится char, потом 3 пустых байта, потом short, тогда как short должен лежать сразу же за char (в x86 выравнивание не нужно).

Double занимает ровно 2 ячейки, так что все Double'ы будут идти встык друг за другом, как и должно быть.

53

Re: AHK: DllCall и правильный вызов функций.

А как вы думаете почему на автохотки не сделают такую же возможность создавать/отправлять структуры без подсчетов смещений и выравниваний пользователем, как на автоит?

54

Re: AHK: DllCall и правильный вызов функций.

Лень, наверно.

55

Re: AHK: DllCall и правильный вызов функций.

В AHK_H есть, а в AHK есть библиотека.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

56 (изменено: Malcev, 2018-12-30 19:43:32)

Re: AHK: DllCall и правильный вызов функций.

А можешь привести пример использования библиотеки для отсылки копии структуры для 32bit struct S2 {char c1;short s;char c2;}?
Разве она не ссылки создает?

Struct alows easy access to the world of structures. Simple to use using object syntax, dynamic structure resolution, pointers support and more.It's now so simple to use structures in AHK like never before.

Struct[""] contains pointer of structure so you can pass it to DllCall and SendMessage functions.

https://autohotkey.com/board/topic/5515 … 412-ahkv2/

57

Re: AHK: DllCall и правильный вызов функций.

Я, честно говоря, ни разу не пользовался, люблю сам считать. Попозже могу посмотреть.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

58 (изменено: Malcev, 2018-12-30 20:07:58)

Re: AHK: DllCall и правильный вызов функций.

Еще интересен момент.

But struct S2 {char c;short s;} would need to be passed as "ptr", &S2 for x64

А если функция ожидает копию структуры?
Или она не может ожидать копию структуры на x64 по спефицикации?

Structs and unions of size 8, 16, 32, or 64 bits, and __m64 types, are passed as if they were integers of the same size. Structs or unions of other sizes are passed as a pointer to memory allocated by the caller.

59

Re: AHK: DllCall и правильный вызов функций.

Так она и будет размером 32 бита, если с выравниванием.

60

Re: AHK: DllCall и правильный вызов функций.

А разве если мы делаем такую операцию "int", c | (s << 16), мы не получаем, что 1 байт занимает char, потом идет пустой байт, а потом 2 байта short?

61

Re: AHK: DllCall и правильный вызов функций.

Ну да, так и должно быть в x64. В x86 надо сдвинуть на 8.

62

Re: AHK: DllCall и правильный вызов функций.

Тогда отвечающий с оффорума ошибся, когда написал, что для cdecl  нужно сдвигать на 16? А вы не могли бы привести пример, как добавить байт в конец, numput здесь же не поможет? И как создать адрес структуры памяти, кратный самому большому элементу?

63 (изменено: YMP, 2018-12-31 17:32:41)

Re: AHK: DllCall и правильный вызов функций.

А зачем байт добавлять? Если вы в трёхлитровую банку нальёте два литра, то надо ли добавлять третий пустой литр? Так же если вы трёхбайтную структуру передаёте как int, то и четвёртый байт там само собой будет. Функция же знает, что она получает и какую часть объёма int занимает полезная информация. Главное чтобы она лежала правильно. Для x64 с выравниванием, для x86 без выравнивания.

Если создаёте структуру, то опять же просто выделяете 4 байта памяти и располагаете char и short в правильных местах этого объёма.

Адрес проще всего подгонять под кратный восьми. Судя по моим экспериментам, VarSetCapacity на x64 и так всегда выдаёт адреса, кратные 8. На x86 вижу либо то же, либо иногда бывают кратные 4.

Если нужна стопроцентная гарантия, то запрашивайте буфер на 7 байт больше, чем нужно, потом к адресу переменной прибавляйте 7 и округляйте результат до 8. Этот адрес уже и используйте для структуры.


VarSetCapacity(v1, sruct_size + 7)
pStruct := ((&v1 + 7) >> 3) << 3

Сдвиг на 3 бита вправо равносилен целочисленному делению на 8 (с отбрасыванием остатка), влево — умножению на 8. На 3 потому, что 8 — это третья степень двойки, а биты же двоичные числа.

В cdecl аргументы в стеке размещаются точно так же, как в stdcall. Разница между ними в том, кто их потом оттуда убирает. В cdecl это делает вызвавший функцию код, а в stdcall сама эта функция.

64

Re: AHK: DllCall и правильный вызов функций.

У меня получается адрес памяти всегда кратным A_PtrSize, при том для 32bit всегда некратным 8.
То есть адрес и так всегда будет кратен самому большому элементу в структуре для 64 бит, а для 32 бит, как вы сказали, это необязательно.
А почему вы прибавляете именно 7?
И почему если мы раскомментируем msgbox то результат будет другим?

sruct_size := 16
VarSetCapacity(v1, sruct_size + 7)
; msgbox % &v1
pStruct := ((&v1 + 7) >> 3) << 3
msgbox % &v1 "`n" pStruct

И создание адреса структуры кратного самому большому элементу необходимо только при посылании структуры как ссылки?
И например такую структуру

struct ST1 {
    char c1;   1
    short s1;   2
    word w; 2
    double d;   8
};

для 32 бит нужно передавать так?

"int64", c1 | (s1 << 8) | (w << 24), "double" d

А для 64 так?

"int64", c1 | (s1 << 16) | (w << 32), "double" d

65

Re: AHK: DllCall и правильный вызов функций.

Malcev пишет:

И почему если мы раскомментируем msgbox то результат будет другим?

Не знаю, а почему это важно?

Malcev пишет:

И создание адреса структуры кратного самому большому элементу необходимо только при посылании структуры как ссылки?

Видимо. В отношении стека я об этом не слышал. Ячейки его и так выровнены.

Malcev пишет:

И например такую структуру для 32 бит нужно передавать так?

А это реальный случай? Что-то я сомневаюсь. Как её передал бы компилятор С, я не знаю. В принципе, наверно, мог бы как угодно, поскольку ведь код той функции, которая эту структуру принимает и использует, тоже он пишет. Он может просто выделить достаточное место в стеке и записать её туда либо ближе к началу этого места, либо ближе к концу, либо посередине. Нам нет смысла гадать.

66

Re: AHK: DllCall и правильный вызов функций.

YMP пишет:

а почему это важно?

Просто интересно такое поведение.

YMP пишет:

А это реальный случай?

Не, выдуманный.
Так всё-таки почему вы именно на 7 байт больше запрашивали буфер?
Кстати, на оффоруме функцию написали, правда для ahk2:
https://github.com/HelgeffegleH/AHK-mis … all_struct

67

Re: AHK: DllCall и правильный вызов функций.

Malcev пишет:

Просто интересно такое поведение.

Ну, видимо, MsgBox тоже использует память.

Malcev пишет:

Так всё-таки почему вы именно на 7 байт больше запрашивали буфер?

Это максимум, на который может сдвинуться вперёд адрес при округлении. Ведь расстояние между круглыми адресами 8 байт.

Malcev пишет:

Кстати, на оффоруме функцию написали, правда для ahk2

При том, что тема была про v1.

68

Re: AHK: DllCall и правильный вызов функций.

На оффоруме автор функции написал, что и в x32 и в x64 элементы структуры нужно выравнивать по границе типа.
Структура {char c;short s;} передается всегда как "int", c | (s << 16).
В википедии вроде также написано:

struct MixedData
{
    char Data1;
    short Data2;
    int Data3;
    char Data4;
};

After compilation the data structure will be supplemented with padding bytes to ensure a proper alignment for each of its members:

struct MixedData  /* After compilation in 32-bit x86 machine */
{
    char Data1; /* 1 byte */
    char Padding1[1]; /* 1 byte for the following 'short' to be aligned on a 2 byte boundary
assuming that the address where structure begins is an even number */
    short Data2; /* 2 bytes */
    int Data3;  /* 4 bytes - largest structure member */
    char Data4; /* 1 byte */
    char Padding2[3]; /* 3 bytes to make total size of the structure 12 bytes */
};

https://en.wikipedia.org/wiki/Data_structure_alignment

69

Re: AHK: DllCall и правильный вызов функций.

Если структура передаётся по ссылке 32-битной WinAPI функции, то не нужно. По крайней мере я никогда этого не делал, и никаких проблем не было. А как там компиляторы внутри программ эти структуры передают, это нас не касается. Для нас важен внешний интерфейс, с которым мы только и имеем дело. А тут требование выравнивания есть только для x64.

Конечно, можно предположить, что все структуры для x86 WinAPI так организованы, что и выравнивать не надо. Ну, может быть. Я каждую специально на этот предмет не проверял. Но это только гипотеза. Надо смотреть конкретные примеры структур.

70

Re: AHK: DllCall и правильный вызов функций.

А у вас есть ссылка на документацию, где написаны эти требования для передачи структур для x64 и для x86?
И если передавать x86 с выравниванием по границе типа, то будет ошибка?

71

Re: AHK: DllCall и правильный вызов функций.

Нет, ссылки нету.

Сейчас проверил вот эту структуру в Visual C++.


typedef struct tagRAWMOUSE {
    USHORT usFlags;
    union {
        ULONG ulButtons;
        struct  {
            USHORT  usButtonFlags;
            USHORT  usButtonData;
        };
    };
    ULONG ulRawButtons;
    LONG lLastX;
    LONG lLastY;
    ULONG ulExtraInformation;
} RAWMOUSE, *PRAWMOUSE, *LPRAWMOUSE;

Расстояние от начала структуры до ulButtons показывает 4 байта. Значит, выравнивание всё-таки есть и в x86. Видимо, верна моя догадка, что большинство структур так составлено, что выравнивание не требуется, поэтому и нет ошибок, даже если про него не думаешь.

72

Re: AHK: DllCall и правильный вызов функций.

Malcev пишет:

такую структуру

struct ST1 {
    char c1;   1
    short s1;   2
    word w; 2
    double d;   8
};

для 32 бит нужно передавать так?

"int64", c1 | (s1 << 8) | (w << 24), "double" d

А для 64 так?

"int64", c1 | (s1 << 16) | (w << 32), "double" d

Для 64 бит нужно передавать ссылкой, так как размер ее не равен 8, 16, 32, or 64 bits.

73

Re: AHK: DllCall и правильный вызов функций.

В общем, это логично. Первые 4 аргумента в x64 передаются в регистрах процессора. Если структура занимает 48 бит, вы не можете её из регистра записать в память как 48 бит, по кр. мере не за одну инструкцию процессора. За одну придётся писать сразу 64 бита. А если структура, к примеру, расположена в самом конце буфера памяти, то вы выйдете на 16 бит за его конец и перезапишете там какие-то другие данные нулями. Если же она находится в конце страницы памяти, а следующая страница не была выделена, то, наверно, будет исключение доступа. Тут даже и попытка чтения, по идее, может его вызвать.

8, 16 и 32 бита разрешены, поскольку эти части 64-битного регистра можно использовать, как самостоятельные регистры. Например, в регистре RAX можно к ним обращаться, как AL, AX и EAX. Так что здесь вы лишнего писать не будете.

74 (изменено: Malcev, 2019-01-21 14:11:42)

Re: AHK: DllCall и правильный вызов функций.

А возможно ли вызвать методы класса из dll?

§ imread()
Mat cv::imread 	( 	const String &  	filename,
		int  	flags = IMREAD_COLOR 
	) 		
Python:
	retval	=	cv.imread(	filename[, flags]	)

https://docs.opencv.org/3.4.5/d4/da8/gr … 7b3bbd3a56
Dll Export Viewer показывает:

class cv::Mat __cdecl cv::imread(class cv::String const & __ptr64,int)

Через NET Framework Interop (CLR, C#, VB) вообще dll не загружается - выводит ошибку.
https://www.autohotkey.com/boards/viewtopic.php?t=4633