News:

MASM32 SDK Description, downloads and other helpful links
MASM32.com New Forum Link
masmforum WebSite

Help with "wsprintf".

Started by Titan, December 24, 2004, 01:10:43 AM

Previous topic - Next topic

Titan

First, I use my Peek function to read the memory of a window.


invoke Peek, P1Mins, ADDR P1MinsData,4


It reads what's at P1Mins offset and puts it into P1MinsData.  I have my data initialized like this:


P1Mins                       equ 4FD4A0h
P1MinsData                 db 4 dup(?)


I then proceed to draw to another window's screen using TextOut and the other API's involved.


invoke TextOut,ScDC,100,100,ADDR P1MinsData,4


And here is where the problem comes in!  It doesn't display my "Mins" as a number.  It displays it as an ASCII equivalent it seems.  I tried to solve this on my own by searching the web.  Here is what I came up with - the wsprintf API supposedly converts integers to strings.  I attempt to use it, but it doesn't seem to be working.


.data
Buffer                 db 4 dup(?)
.code
invoke wsprintf,ADDR Buffer,ADDR P1MinsData
invoke TextOut,ScDC,100,100,ADDR P1MinsData,4


But... it doesn't work.  It prints to the other window something like "||||" - which I assume is some ASCII equivalent for 00h.  I would hope someone can please help me with this, since I've been stuck on this for hours.


Thanks,
Titan

hador

Hi Titan...


.data
  buf db 5 dup(0)
  fil db "%d",0
 P1MinsData dd 0

.const
  P1Mins equ 4FD4A0h

.code
  invoke Peek, P1Mins, ADDR P1MinsData, 4
  invoke wsprintf,  ADDR buf, ADDR fil, P1MinsData
  invoke TextOut,ScDC,100,100,ADDR buf ,4




--Hador

Titan

#2
Hi Hador, I tried your code, but when I go to do the TextOut, it doesn't display in the window.  I also have added another line of code to it.

invoke Peek, P1Mins, ADDR P1MinsData, 4
invoke wsprintf, ADDR buf, ADDR fil, P1MinsData
invoke TextOut,ScDC,100,100,ADDR buf ,4

invoke SetWindowText,hwndEdit,ADDR buf       'Put ADDR buf in my edit box

And nothing appears in the edit box either.  Not sure what's wrong here, thanks for the help.

Edit: It seems like nothing after the "invoke wsprintf" is working.  I changed the textout ADDR to my AppName and it doesn't display.  I'm still very new to assembly, but I'm sure this will help you out.

Edit#2: So I added an "invoke ExitProcess,0" after the "invoke wsprintf", and nothing happens, which further proves something funny is going on here.  I hope you guys can figure out what. :)

donkey

#3
You should use the return value from wsprintf to set the buffer length for TextOut,it returns the number of characters in the buffer...

invoke Peek, P1Mins, ADDR P1MinsData, 4
invoke wsprintfA,  ADDR buf, ADDR fil, [P1MinsData]
or eax,eax
jz > ; skip if buffer is empty
invoke TextOutA,[ScDC],100,100,ADDR buf ,eax
:


Check the value of EAX coming from wsprintf to verify that something is being written into the buffer. You might try to display the output in a message box to help debug the code, that way you can tell whether the problem is with wsprintf. Also you should be verifying that the value of P1MinsData is never greater than 9999 or the buffer will be too small, you might want to create at least a 12 byte buffer just in case (max = 0FFFFFFFFh or 10 decimal digits).

No need to put the P1Mins constant in the CONSTANTS section, it just uneccessarily bloats your execuatable by adding 512 bytes for the section when it is not used.

I imagine the Peek function just looks at memory at a specific address, why do you need a proc to do this if you are only getting a DWORD ? You can just do it directly as follows...

P1Mins equ 4FD4A0h

invoke wsprintfA,  ADDR buf, "%u", [P1Mins] ; push the value in P1Mins directly
or eax,eax
jz > ; skip if buffer is empty
invoke TextOutA,[ScDC],100,100,ADDR buf ,eax
:


Note that the code snippet above will probably not work with MASM as that assembler does not properly handle addressing using constants. You will have to do it like this...

P1Mins equ 4FD4A0h

mov eax, P1Mins
invoke wsprintfA,  ADDR buf, ADDR fil, DWORD PTR [eax]
or eax,eax
jz @F ; skip if buffer is empty
invoke TextOutA,[ScDC],100,100,ADDR buf ,eax
@@:


This is because even though you specify an address using the constant, MASM will ignore the [] and just move the constant into EAX, one of the many prices you pay for MASM ignoring the [].
"Ahhh, what an awful dream. Ones and zeroes everywhere...[shudder] and I thought I saw a two." -- Bender
"It was just a dream, Bender. There's no such thing as two". -- Fry
-- Futurama

Donkey's Stable

Titan

See, I would check the return value of the function to make sure it's writing to the buffer .. BUT like I mentioned earlier, no code after the "invoke wsprintf" is being executed.


...
invoke ExitProcess,0
wsprintf, ADDR buf, ADDR fil, [P1MinsData]
...


My applicationwill exit.


...
wsprintf, ADDR buf, ADDR fil, [P1MinsData]
invoke ExitProcess,0
...


My application won't exit.

So it's kind of hard to test the return value or make it appear in a message box when nothing after it will execute. :(

donkey

Hi,

In your first post you said...

QuoteBut... it doesn't work.  It prints to the other window something like "||||" - which I assume is some ASCII equivalent for 00h.  I would hope someone can please help me with this, since I've been stuck on this for hours.

So I assumed that since you got output that wsprintf was executed and you obtained a result. It sounds like a buffer over-run problem so just increase the size of the buffer and try it out...

P1MinsData                 db 12 dup(?)
"Ahhh, what an awful dream. Ones and zeroes everywhere...[shudder] and I thought I saw a two." -- Bender
"It was just a dream, Bender. There's no such thing as two". -- Fry
-- Futurama

Donkey's Stable

Sparafusile

With four bytes, you'd have values anywhere from 0 to 4294967295 (if you're using unsigned output, which you appear to be), so your buffer needs to be at least 11 bytes long (10 for the number and one more for the NULL terminator).  wsprintf will automaticaly append a NULL to your buffer so you need to allow for that.  The number of characters to output with TextOut will be anywhere from 1 to 10, so check the return value of wsprintf like donkey suggested.

Just FYI, if you decide to use signed numbers, you'll have numbers from -4294967168 to 4294967167 which means you'll need a buffer of atleast 12 bytes in order to hold the sign, value, and NULL.

Spara

Titan

Quote from: donkey on December 24, 2004, 06:51:18 AM
QuoteBut... it doesn't work.  It prints to the other window something like "||||" - which I assume is some ASCII equivalent for 00h.  I would hope someone can please help me with this, since I've been stuck on this for hours.

So I assumed that since you got output that wsprintf was executed and you obtained a result. It sounds like a buffer over-run problem so just increase the size of the buffer and try it out...

Sorry I wasn't specific, before I used wsprintf, TextOut would display ASCII characters instead of the real value I'm suppose to be reading.  I searched the internet and found similiar people who have the same problem in C++.  They recommended using wsprintf to convert an integer to a string for TextOut... and that's why I'm using it.  But, I'm yet to see any results with it.  I tried upping the buffer size to 12, and it still doesn't continue any further with the execution of my code after the "invoke wsprintf"(I tried ExitProcess test again).  Anymore suggestions? :-/

MichaelW

#8
QuoteI tried upping the buffer size to 12, and it still doesn't continue any further with the execution of my code after the "invoke wsprintf"(I tried ExitProcess test again).

wsprintf is one of the few API functions that uses the C calling convention rather than the STDCALL convention.

MSDN: wsprintf Function

Unless the function is prototyped as it is in the MASM32 user32 include file:

wsprintfA PROTO C :DWORD,:VARARG
wsprintf equ <wsprintfA>

You must add code to remove the parameters from the stack after the function returns. If you pass three arguments, then you will need to add an

add  esp, 12

statement after the invoke statement. Considering that wsprintf takes a variable number of arguments, using the proper prototype would make the coding both easier and less prone to error because it would allow MASM to count the arguments and encode the stack adjustment for you.
eschew obfuscation

donkey

Hi Titan,

Is it possible to post your test code ? The wsprintfA function works OK and the code you were supplied should also work since you are obviously using MASM. That would generally mean that it's something else that's doing it and from the information you have given we cannot tell you the problem. A quick sample project that demonstrates the problem would be best or at least some background information like is the DC in another application, what type of control are you attempting to write to, what does the Peek funciton do ?
"Ahhh, what an awful dream. Ones and zeroes everywhere...[shudder] and I thought I saw a two." -- Bender
"It was just a dream, Bender. There's no such thing as two". -- Fry
-- Futurama

Donkey's Stable

Titan

#10
Okay, I'll provide a more detailed post of my application.


.const

ClassName                          db 'DLGCLASS',0
AppName                           db 'Dialog as main',0
AboutMsg                           db 'MASM32 RadASM Dialog as main',13,10,'Copyright © MASM32 2001',0
ScWindow                          db 'Brood War',0
ScText                               db 'Player #          Mins          Gas          Supply',0
buf                                    db 12 dup(?)
fil                                      db "%i",0

P1Mins                               equ 4FD4A0h


I know I have some stuff I can delete, but I'm not worried about that right now.



.data?
hInstance                          dd ?
CommandLine                    dd ?
hWnd                               dd ?
ScWnd                              dd ?
ScDC                                 dd ?
hProcess                            dd ?
hwndEdit                           HWND ?
P1MinsData                        dd ?


I then proceed to create my Peek function, to read the address of another window(in this case Brood War).



Peek PROC aHack:DWORD, nVal:DWORD, iSize:BYTE
LOCAL phandle:DWORD
LOCAL pid:DWORD
LOCAL windhand:DWORD
Invoke FindWindow, NULL, addr ScWindow
mov windhand,eax
Invoke GetWindowThreadProcessId, windhand, addr pid
Invoke OpenProcess,PROCESS_ALL_ACCESS, 0, pid
mov phandle,eax
invoke ReadProcessMemory,phandle, aHack, nVal, iSize, NULL
ret
Peek ENDP



Then when you click a button, I set a timer:


invoke SetTimer,hWnd,1,1,DoPaint


The timer does this(for now):



DoPaint Proc

invoke FindWindow,NULL,ADDR ScWindow
mov ScWnd,eax
invoke GetDC,ScWnd
mov ScDC,eax
invoke SetBkMode,ScDC,TRANSPARENT
RGB    200,200,50
invoke SetTextColor,ScDC,eax
RGB    0,0,255
invoke SetBkColor,ScDC,eax
invoke TextOut,ScDC,0,0,ADDR ScText,SIZEOF ScText
  invoke Peek, P1Mins, ADDR P1MinsData, 4
 
  invoke wsprintf,  ADDR buf, ADDR fil, P1MinsData
 
invoke ExitProcess,0
  invoke TextOut,ScDC,100,100,ADDR buf ,4
 
invoke SetWindowText,hwndEdit,ADDR buf

DoPaint endp



I find the ScWindow, get it's DC, set it's BkColor and Text, and then proceed to paint "ScText"(which does work).  Then I Peek the memory of Brood War, and I should be getting an integer between 1-10000.  I want to TextOut that integer I get.

As you can see, I have an ExitProcess in there, but my application doesn't close(because like I've stated it doesn't continue doing anything after "invoke wsprintf".

I hope this helps, I can always post my full project if needed.

Thanks,
Titan

John

#11
Here is a special note in the Platform SDK regarding wsprintf:
Quote Security Alert  Using this function incorrectly can compromise the security of your application. The string returned in lpOut is not guaranteed to be NULL-terminated. Also, avoid the %s format -- it can lead to a buffer overrun. If an access violation occurs it causes a denial of service against your application. In the worse case, an attacker can inject executable code. Consider using one of the following alternatives: StringCbPrintf, StringCbPrintfEx, StringCbVPrintf, StringCbVPrintfEx, StringCchPrintf, StringCchPrintfEx, StringCchVPrintf, or StringCchVPrintfEx. You should review Security Considerations: Windows User Interface before continuing.
I have put in bold type something that could potentially be your problem since it seems your application never returns once calling wsprintf.

MichaelW

Titan,

I don't know the exact mechanism by which it is failing (or how to determine this), but the problem in the most recent code that you posted is that wsprintf is trying to write to a buffer that is located in a segment with a read-only attribute.

eschew obfuscation

Titan

A big thanks to everyone here who helped me, this is a great forum.  I saw what you wrote, MichaelW, about my buffer being in the "const", and therefore being read-only.  I moved it to the .data? section and all is working. :U

Thanks again,
Titan

ofg

hi Titan,

     Take a look at also:
      Iczelion Tutorials.Chapter 31
              ListView Controls
       String2Dword    Procedure
        check the return value
    regards.