Hi

I have read MSDN and I have seached the board and I have studied examples but I can't get the hang of how "wsprintf" really works. Can someone explain how it works.

This is my problem. I have following string



pString=
004030EE 24 47 50 52 4D 43 2C 31 $GPRMC,1
004030F6 33 32 38 35 32 2C 56 2C 32852,V,
004030FE 35 39 32 35 2E 35 32 30 5925.520
00403106 35 2C 4E 2C 30 31 38 32 5,N,0182
0040310E 30 2E 35 33 31 31 2C 45 0.5311,E
00403116 2C 30 2E 30 2C 30 2E 30 ,0.0,0.0
0040311E 2C 31 36 30 32 30 33 2C ,160203,
00403126 34 2E 32 2C 45 2C 53 2A 4.2,E,S*
0040312E 31 35 00 00 00 00 00 00 15......


Now I want to write a part of this string to an editcontrol. So I write this code.



mov edi,offset pString
mov ax,word ptr [edi+7]
push eax
mov ax,word ptr [edi+9]
push eax
mov ax,word ptr [edi+11]
push eax
invoke wsprintf,addr lpOut,SADD('%hd:%hd:%hd')
invoke SetDlgItemText,hWin,IDC_EDT2,addr lpOut


This is what turns up in the editcontrol: 12853:14386:13105
What I want is: 13:28:52 i.e. time

Are there any experts on wsprintf on the board?

Regards
Posted on 2003-02-16 07:51:03 by minor28
Has possibly nothing to do with wsprintf but with your code: just clear hiword of eax at first
Posted on 2003-02-16 08:18:56 by japheth
wsprintf seems to be doing EXACTLY what you are telling it to do. If you look at your hex dump, the first WORD value you load from into ax is 3331h which is = 13,105 in the decimal system returned by wsprintf.

Similarly, the value at is 3832h = 14,386
and the value at is 3235h = 12,853

which are all the values returned correctly by the wsprintf function.

In simple words, wsprintf expects integer values as input, NOT text.

Raymond
Posted on 2003-02-16 08:32:47 by Raymond
This should work...

mov edi,offset pString

[color=red]add edi, 11
push edi
sub edi, 2
push edi
sub edi, 2
push edi[/color]
invoke wsprintf,addr lpOut,SADD('%[color=red].2s[/color]:%[color=red].2s[/color]:%[color=red].2s[/color]')
[color=red]add esp, 1Ch[/color] [color=green];clean up the stack !![/color]
invoke SetDlgItemText,hWin,IDC_EDT2,addr lpOut


If you dont understand why it works now, lemme know and ill explain ;)
Posted on 2003-02-16 09:12:42 by BubbaFate
From what I can see, you are probably converting some variable data to decimal representation in memory before having to send that info to a dlg control.

You can probably save yourself later coding and problems by inserting the desired ":" and a terminating 0 at the same time. Then simply supply the address of that already formatted string to the SetDlgItemText function without relying on wsprintf.

Raymond
Posted on 2003-02-16 11:01:47 by Raymond
Thanks for your replys

japheth:
Eax is zeroed earlier. That's not the problem

Raymond:
You are right I don't know what to tell wsprintf to do. That's my question.

BubbaFate:
I see that you push the addresses to the stack instead of the values that I do. Is that the correct way? I suppose it is, because the editcontrol shows the right time. I understand what you have done but is it always the addresses that should be pushed on the stack?

However cleaning the stack makes the editcontrol show ".|@".Without cleaning it works. Why should I clean the stack?

Regards
Posted on 2003-02-16 11:23:02 by minor28
When wsprintf encounters %s then it expects a string argument, and you always pass strings by their address... hence why i keep pushing edi onto the stack. Remember that the charater '1' is not the same as the value 1, character '1' is actually the value 31h.

The fact that adding a value to esp is causing your textbox to display a different value is mind boggling... I have no idea why its doing that... it doesn't do it on my computer...

And finally, the reason you have to clean up the stack is because wsprintf uses C calling convention, which means the caller must clean up the stack... why does it do this? well it has to, because wsprintf has no way of knowing how many arguments you passed it, therefore its impossible for wsprintf to know how many bytes to pop off the stack. When you use invoke it will automatically put the correct add esp, ??? but since you manually put 3 arguments on the stack, you have to pop them back off (AKA add esp, 1Ch)
Posted on 2003-02-16 11:35:06 by BubbaFate
Yep im stupid... its not

add esp, 1Ch

its

add esp, 0Ch

sorry about that :rolleyes:

In fact its probably better to do something like this...

add esp, sizeof(DWORD) * 3
Posted on 2003-02-16 11:39:25 by BubbaFate
Thanks for the explanations. Now I think I can handle wsprintf. One more question



int wsprintf(
LPTSTR lpOut, // output buffer
LPCTSTR lpFmt, // format-control string
... // optional arguments
);


what could "optional arguments" be? What is the good of that?

Regards
Posted on 2003-02-16 12:33:34 by minor28
it means there are two arguments that you have to pass to wsprintf and you can pass more if you want... this goes back to the c calling convention thing...

basically you pass however many % signs you have in your format string + 2 arguments
Posted on 2003-02-16 12:35:26 by BubbaFate
I am not familiar with C language just a little VB and VBA. Masm i a hobby of mine.

I don't quite understand your answer. Am I right in this. Instead of pushing arguments to the stack you can use the optional arguments of the wsprintf function. I have studied Iczelion's PE tutorial no 7 where I found a most complicated wsprintf namely


invoke wsprintf, addr temp,\
addr ExportTable,\
eax,\
[edi].nBase,\
[edi].NumberOfFunctions,\
[edi].NumberOfNames,\
[edi].AddressOfFunctions,\
[edi].AddressOfNames,\
[edi].AddressOfNameOrdinals


Here we have 7 optional arguments.

With a template like


ExportTable db 0Dh,0Ah,"======[ IMAGE_EXPORT_DIRECTORY ]======",0Dh,0Ah
db "Name of the module: %s",0Dh,0Ah
db "nBase: %lu",0Dh,0Ah
db "NumberOfFunctions: %lu",0Dh,0Ah
db "NumberOfNames: %lu",0Dh,0Ah
db "AddressOfFunctions: %lX",0Dh,0Ah
db "AddressOfNames: %lX",0Dh,0Ah
db "AddressOfNameOrdinals: %lX",0Dh,0Ah,0

the result is


======[ IMAGE_EXPORT_DIRECTORY ]======
Name of the module: a3d.dll
nBase: 1
NumberOfFunctions: 5
NumberOfNames: 5
AddressOfFunctions: 9F98
AddressOfNames: 9FAC
AddressOfNameOrdinals: 9FC0


The first optional argrument EAX is the address of the dll name, i.e. "a3d.dll"

So my conclusion is that you can use optional argument instead of pushing. Is that a correct understanding?

Regards
Posted on 2003-02-16 13:46:52 by minor28

Instead of pushing arguments to the stack you can use the optional arguments


There is only one way to pass anything to a function, and thats pushing them onto the stack. Therefore "pushing arguments to the stack" and "you can use the optional arguments" is the same thing.

push [color=magenta]eax[/color] [color=green]; feed an argument to wsprintf[/color]

push [color=magenta]ecx[/color] [color=green]; feed another argument[/color]
invoke wsprintf, addr szBuffer, addr szFormat
add esp, sizeof(dword) * 2 [color=green]; fix the stack since we manually pushed some arguments[/color]


is the same thing as...

push [color=magenta]eax[/color] [color=green]; feed an argument to wsprintf[/color]

invoke wsprintf, addr szBuffer, addr szFormat, [color=magenta]ecx[/color]
add esp, sizeof(dword) * 1 [color=green]; fix the stack since we manually pushed an argument[/color]


is the same thing as...

invoke wsprintf, addr szBuffer, addr szFormat, [color=magenta]ecx[/color], [color=magenta]eax[/color]
Posted on 2003-02-16 14:01:21 by BubbaFate
Lemme explain about c calling convention a little bit... it doesn't have anything to do with the language "C" (well maybe it was developed because of the c language or something, i dunno) But basically your going to encounter two types of calling convetions... "standard call" and "c"

Most of the api functions use standard call which means the caller pushes arguments onto the stack in reverse order, and the callee cleans the stack... so here is some pseudo code...



main proc
push arg3
push arg2
push arg1
call myfunc
main endp

myfunc proc
add esp, 0Ch ; 3 dword arguments were passed so I add 3 * 4 or 0Ch to esp to 'pop' those arguments off
ret
myfunc endp


C calling convention means the caller pushes arguments onto the stack in reverser order (just like stdcall), and the caller cleans the stack... so here is some pseudo code...



main proc
push arg3
push arg2
push arg1
call myfunc
add esp, 0Ch
main endp

myfunc proc
; i have no idea how many arguments were passed... gotta let the caller clean up
ret
myfunc endp


Its impossible for wsprintf to use standard call because there is no way for wsprintf to know how many arguments you passed to it, think about it... if you were tasked to write the function wsprintf what would you add to esp in order to clean up the stack? "add esp, ???" it would depend on how many arguments were passed, and only the caller knows how many arguments its passing.
Posted on 2003-02-16 14:32:36 by BubbaFate
Thanks

Your pushing explaination made it all clear for me. I ought to have thought of the obvious in pushing and the arguments in invoke expression and thanks for the calling convetions explaination.

OK I am more than satisfied. Thanks for taking time to inform me.

Regards
Posted on 2003-02-16 14:38:41 by minor28
BubbaFate

Most of the api functions use standard call which means the caller pushes arguments onto the stack in reverse order, and the callee cleans the stack... so here is some pseudo code...


code:--------------------------------------------------------------------------------
main proc
push arg3
push arg2
push arg1
call myfunc
main endp

myfunc proc
add esp, 0Ch ; 3 dword arguments were passed so I add 3 * 4 or 0Ch to esp to 'pop' those arguments off
ret
myfunc endp--------------------------------------------------------------------------------



Sorry to disagree on this one. If you try to run that code, you may get a page fault trying to return from myfunc to an address having the value of arg3 in your example.

The proper way is to instruct the CPU to adjust the ESP after the return with a:

ret nBytes

In your example, the nBytes would be 12 (i.e. 3*4)

The function thus returns to the proper address (residing at the top of the stack) and the CPU cleans the stack according to the instruction.

Raymond
Posted on 2003-02-16 20:46:27 by Raymond
yep, your right... thanks for pointing that out :alright:
Posted on 2003-02-16 20:52:21 by BubbaFate
BubbaFate

Hey, don't feel bad about it. You know that I know that you know that it was incorrect.

I've been known to make similar errors, specially when I try to rush a reply. This may not be all that bad because I think that those errors, when corrected, may have some additional educational value.

Raymond
Posted on 2003-02-18 00:19:16 by Raymond