News:

Masm32 SDK description, downloads and other helpful links
Message to All Guests
NB: Posting URL's See here: Posted URL Change

Main Menu

Length Error when calling DLL from high level language

Started by markB, June 25, 2017, 05:40:16 AM

Previous topic - Next topic

markB

Thanks very much for your reply.  Because a deadline is fast approaching, I had to re-write this differently, and now it runs and serves my needs in this case. 

I'll come back to this thread later, but I want to thank everybody who answered my question -- you all posted very helpful information. 


aw27

Quote from: markB on June 25, 2017, 12:09:44 PM
Thanks very much for your reply.  Because a deadline is fast approaching, I had to re-write this differently, and now it runs and serves my needs in this case. 

That's a cool idea to rewrite it from scratch, particularly because nobody uses "USES EBP" in this World.  :icon_rolleyes:
In spite of that, the apparent cause for the error was that you can not index arrays in MASM as you do in any other language. In MASM you must take into account the size of the element itself (in MASM they call it TYPE, so the confusion starts just here).

As an illustration:


.686
.model Flat, Stdcall
option Casemap :None 

includelib h:\masm32\lib\msvcrt.lib ; (*)
printf PROTO C :VARARG

Counter=0

.data

format db "%d",13,10,0
someStruct STRUCT
REPEAT 1024
Counter = Counter + 1
dword Counter
ENDM
someStruct ENDS

myStruct someStruct <>

.code

TM460_Pvtx_Values_Sub1 Proc USES ebx edi Last20EBands1:PTR DWord
mov edi, Last20EBands1
xor ebx, ebx
.while ebx<sizeof(someStruct)/TYPE(DWORD)
mov edx, dword ptr [edi+ebx*TYPE(DWORD)]
INVOKE printf, offset format, edx
inc ebx
.endw
ret
TM460_Pvtx_Values_Sub1 endp

main PROC
INVOKE TM460_Pvtx_Values_Sub1, offset myStruct
ret
main ENDP
END main



(*) Replace as needed. Someone may tell that you should use  \masm32\lib\msvcrt.lib because relative addresses work all the time. Of course, they do not when you scatter things all over the place,
Build with:
"C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\bin\ml" /c /coff test.asm
"C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\bin\link" /SUBSYSTEM:console test.obj

coder

Quote from: markB on June 25, 2017, 05:40:16 AM
I have written a DLL in MASM to call from a high-level language.  The proc assembles correctly and the DLL is created and placed in the proper directory. 

When I call this DLL, I get the following error:  Length Error. 

Here is the PROC header for the MASM code.  It returns a pointer to the variable BlueLineConfig. 

_____

TM460_Pvtx_Values_Sub1 Proc USES ebp Last20EBands1:PTR DWord

mov ebp,Last20EBands1
BlueLineConfig dd 4 dup (0)
mov edi,OFFSET BlueLineConfig

mov eax,DWORD PTR[ebp+114]
mov ebx,DWORD PTR[ebp+108]

[remaining code]

* * *
mov eax,[edi]

RET

TM460_Pvtx_Values_Sub1 EndP

end LibMain
_____

This Proc takes only one input, an array of 140 elements, in DWord form.  I can't see why I would get a Length Error when I am passing exactly what I specified. 

Thanks very much for any help.

I don't understand what your code does, but my initial guess is that you do not fully understand how the real, low-level codes of a "proc" works. For example, EBP is a taboo register once high-level proc is involved, especially when you have both local variables and function arguments in the function. Look;

push ebp
mov ebp,esp
push ebp                  ;your attempt to save EBP
sub esp,12
mov eax,[ebp+8]                    ;first argument
mov ebp,something_else
mov dword ptr[ebp-8],'ABC'   ;local variable. Now EBP is corrupt due to previous line
mov ebx,[ebp+12]                  ; second argument also has corrupt EBP.


So while you think you have saved the EBP, the truth is, your "proc" uses it internally.


jj2007

Quote from: coder on June 25, 2017, 08:37:46 PMmy initial guess is that you do not fully understand how the real, low-level codes of a "proc" works.

@Mark: Ignore this, it's wrong. Test it:include \masm32\include\masm32rt.inc

.code
MyTest proc uses esi edi ebx ebp hwnd:DWORD, uMsg:DWORD, wParam:DWORD, lParam:DWORD
LOCAL MyLocVar:DWORD
  print str$(hwnd), 9, "hwnd", 13, 10
  print str$(uMsg), 9, "uMsg", 13, 10
  print str$(wParam), 9, "wParam", 13, 10
  print str$(lParam), 9, "lParam", 13, 10
  ret
MyTest endp

start:
  invoke MyTest, 11111, 22222, 33333, 44444
  inkey "ok?"
  exit

end start


It is quite unusual to save ebp but the code would work. Your problem is elsewhere, probably in the way you call the DLL from Python.

aw27

Quote from: jj2007 on June 25, 2017, 08:45:50 PM
It is quite unusual to save ebp but the code would work. Your problem is elsewhere, probably in the way you call the DLL from Python.
Jochen,
In his particular case works simply because he loads only once a variable from the stack to the ebp register. mov ebp, [ebp+8]. In general it would not work when you use ebp as the frame base pointer.  :t

hutch--

The general idea would be to fix all of the register usage first, put it into a conventional procedure with a stack frame and make sure you properly observe the Intel "Application Binary Interface" for Win32. If you don't do this you get unexpected crashes as the OS expects certain registers not to be changed between procedure calls. Nor is it sensible to just experiment with one OS version because there is no garrantee that it will be the same in another version. The only safe way is to properly observe the previously mentioned ABI. I would imagine that Python properly observes the Intel ABI so you should be safe in doing the same.

Get it up and going reliably and if there is some need, you can do things like removing the stack frame to reduce the call overhead but be aware that this only matters on very short procedures, on longer ones and ones that call other procedures it is a waste of time.

jj2007

Quote from: aw27 on June 25, 2017, 09:32:53 PM
Jochen,
In his particular case works simply because he loads only once a variable from the stack to the ebp register. mov ebp, [ebp+8]. In general it would not work when you use ebp as the frame base pointer.  :t

Right: You cannot use ebp as a variable and use local variables. But the usage of ebp as such does no harm as long as you don't use any locals and/or arguments.

I posted here a Python script using a simple DLL. It's messy, to say the least. Python does strange things with strings under the hood 8)

However, Mark's problem seems very easy to solve - exchanging ebp with ebx (or esi, edi) should be sufficient. The mechanism of passing arguments as such works perfectly.

aw27

Quote from: jj2007 on June 25, 2017, 09:59:04 PM
I posted here a Python script using a simple DLL. It's messy, to say the least. Python does strange things with strings under the hood 8)
Teachers and people from universities are so proficient in Python, Perl and eventually F (and some in Javascript as well, grrrr) that I don' t dare to argue with them about the virtues of other stuff.  :badgrin:

nidud

#23
deleted

jj2007


coder

Quote from: jj2007 on June 25, 2017, 08:45:50 PM
Quote from: coder on June 25, 2017, 08:37:46 PMmy initial guess is that you do not fully understand how the real, low-level codes of a "proc" works.

@Mark: Ignore this, it's wrong. Test it:include \masm32\include\masm32rt.inc

.code
MyTest proc uses esi edi ebx ebp hwnd:DWORD, uMsg:DWORD, wParam:DWORD, lParam:DWORD
LOCAL MyLocVar:DWORD
  print str$(hwnd), 9, "hwnd", 13, 10
  print str$(uMsg), 9, "uMsg", 13, 10
  print str$(wParam), 9, "wParam", 13, 10
  print str$(lParam), 9, "lParam", 13, 10
  ret
MyTest endp

start:
  invoke MyTest, 11111, 22222, 33333, 44444
  inkey "ok?"
  exit

end start


It is quite unusual to save ebp but the code would work. Your problem is elsewhere, probably in the way you call the DLL from Python.

JJ

I don't quite follow which part of my advice is wrong. EBP is a base pointer register, extremely common in stack programming. I thought you knew...



hutch--

Something I have done in the past is copy arguments to registers from the EBP based addresses then push EBP, use it then pop it but it is risky stuff. You can also do old trick from the win3 days of DLLs and store registers in global variables and while its not elegant, it does work OK.