Getting the following error message:
"use square brackets to address memory, address , offset to get address"
This is the offending code: cmp D[iMsg], WM_CREATE
Wndproc FRAME hWnd, iMsg, wParam,lParam
The Wndproc is before the cmp.
This same code appears in Donkeys Except and FontDlg.
What is D?
cmp iMsg, WM_CREATE
should work.
"D" in GoAsm represents a DWORD
Do you have WM_CREATE defined? It's (I'm pretty sure) in winuser.h
GoAsm thinks WM_CREATE is a label in your program.
Do you have these in your main assembly file?:
#define LINKFILES
#include "windows.h"
(winuser.h will be loaded by windows.h)
Ah. I didn't realize I was in GoAsm.
Carry on.
shankle,
Look through those header files. It really helps later. For example, next time you get that message you'll know it was something you forgot to define. If you aren't using them now--get Donkey's headers.
http://donkeysstable.com/ (http://donkeysstable.com/)
Hi Satpro,
No I did not have them defined. Was trying to use "EQUs" to do the job.
I included them in the program and got other opportunities.
Have no idea where #define linkfiles is coming from.
Anyway here is what I added to my program.
Const Section
#include winuser.h
#include windows.h
#define linkfiles
These are the errors that windows.h caused:
Line 299 of the include file Windows.h could not open file #include "windef.h"
Line 303 of the include file windows.h - unexpected material <ntddi-win9xall>.
WM_CREATE and others are in "winuser.h". Got the following error in winuser.h:
"winuser.h - could not evaluate expression in conditional directive - winver".
Is it possible that these files don't work in Windows 7??????
Nothing is ever easy.....
Thank you for helping - I'm so green
The files do work on Windows 7. Windows.h should be listed first, and as mentioned in Reply #3, winuser.h will be included so you do not need that line. Also, symbols are case sensitive, so use LINKFILES - this is a link switch which "If defined passes the name of the appropriate DLL to GoLink at compile time". Order is important too, so this needs to be defined before including windows.h.
Quote from: shankle on June 26, 2012, 04:38:20 AM
Anyway here is what I added to my program.
Const Section
#include winuser.h
#include windows.h
#define linkfiles
I think it's better to put these includes and defines before all sections.
you shouldn't need to open a CONST section for those
- nor for equates (i see that quite often :P )
I have disabled "windows.h". If I run with it I get errors in windows.h.
I have gone into the program and added an "A" to every API in the program.
When I link they all show up as unresolved.
More than that these items also show up as unresolved:
cbsize, hwnd, msg, msh.param, hdc, FF_ROMAN, hdcPrn
??
LINKFILES is used within the header files. Without "windows.h", since not every API function has an ANSI/Unicode variant, it sounds like you need to list the required DLL(s) that you import from (ex. Kernel32.dll, User32.dll, Gdi32.dll) to the command line for GoLink.
FF_ROMAN is defined in the header files (wingdi.h). As for the rest, those look more like they should have been defined within your source files.
Hi Wjr,
Thanks for helping.
If I understand you correctly, that would make the GoAsm command line Huge. There are about
two dozen of them. I really don't think I understand.
I will check out the wingdi.h for FF_ROMAN.
Correct - if you have that many and are not using the header files with LINKFILES, some alternatives to simplify things would be (see GoAsm+GoLink documentation for details) use of the following in your source:
#dynamiclinkfile path/filename
or a command file for use with GoLink:
GoLink @commands.txt
Thank you WJR for your patience.
I just found the GOLINK manual. Thought every bit of documentation was in the
GoAsm manual. No wonder I was having problems.
It all comes back to not having the necessary header files included, if a data type is used (ie: struct) then the header file which declares it is needed. Otherwise any reference to the type will flag an error, whether it be the type itself or declaring an instance of it/accessing an instant of it.
It's been the same for every language/tool i've coded with, i could be wrong but i don't think it would be any different for GoAsm would it?
HR,
Ghandi
shankle,
Reading through your posts again it occurred to me that you might be having problems with the header files because of a directory issue. When I first set up the files on my computer nothing would work right. It turned out I was in the wrong directory to pull the headers. Then I added something like this in a .bat file:
cls
set INCLUDE=P:\GoAsm\Includes
GoAsm /l /b /c "dd.asm"
GoLink "dd.obj"
The "set" line fixed the problem. But disregard the "P:\GoAsm\Includes" part. Use your include directory instead. "cls" just clears the console so you know where you're at while compiling.
Thanks guys for all your efforts to help me.
All of the header files from Mr. Hansen's site are on my puter as follows:
GoAsm(g:) \include
I made a file to put the includes in: MyProgbrs.h #include \include\windows.h
This is what's in the Assembler program: #define LINKFILES, #include MyProgbrs.h
The batch file is as follows: MyProgbtch.bat, set include, GoAsm(g) /l/b/c \include\
MyProgbrs.h MyProg.asm
Not trying to link as yet.
This is what I got when running MyProgbtch.bat:
Warnings....
line 299 of the include file \include\windows.h: - Could not open file:- #include
"windef.h"
Line 303 of the include file \include\windows.h:- Must declare a section first -
Use DATA SECTION ["name"}, CODE SECTION ["name"] or CONST SECTION
["name'] or just DATA, CODE or CONST: .DATA, .CODE or .CONST
hi Jack,
what you may need to do is to set an environment variable
My Computer - Properties (or Control Panel - System icon)
Advanced Tab
Environment Variables button
you can create variables that will apply to the current user, or system-wide (applies to all users)
according to Edgar's notes...
QuoteYou must create an INCLUDE environment variable with the path to these header files
See "Include files - using #include and INCBIN" in the GoAsm manual for details
so you want something like
INCLUDE=G:\GoAsm\include
Can you do a copy/paste and show exactly what your set line looks like? It's definitely the INCLUDE variable, and you can do it in the batch, which is slightly more convenient, or you can make an environment value, as stated above. But it's definitely the INCLUDE...
Edit: Also, once you set that variable up you won't need the "\include\" added to your #include statements. You can just go:
#include "windows.h". The INCLUDE variable does the subfolder work for you.
Set INCLUDE
GoAsm /l/b/c \include\MyProgbrs.h MyProg.asm
;GOlINK /unused "MyProg.obj"
yep, Think it should be:
GoAsm /l/b/c \MyProgbrs.h MyProg.asm
If I understand you all correctly the Set command should be as follows:
Set INCLUDE=G:\GoAsm\include
Guessing!!!
correct...
if you do:
set INCLUDE
with no parameter, it simply displays the current value of that variable
if you do:
set INCLUDE=
with no parameter, it removes the variable from the environment table
shankle,
Everybody does things a bit differently, but this is sort of how I do it, using your directory structure:
The batch file:
----------------
cls
set INCLUDE=G:\GoAsm\include
GoAsm /l /b /c "MyProg.asm"
GoLink "MyProg.obj" (sometimes you'll put a dll here, after a space, e.g. ddraw.dll)
In the assembly file My Prog.asm:
------------------------------------
(at the top of the file--first)
#define LINKFILES
#define DONKEYLIB_H
#include "windows.h" ;Windows defs
#include "macros.h" ;GoAsm macros (incl. CoInvoke)
#include "ddraw.h" ;only if you use it
#include "MyProgbrs.h" ;your header files...
#include "some_other_assemfile.asm"
DATA SECTION
today dd 29
tomorrow dd ?
nextweek dd today+7-30
CONST SECTION
June = 6
July = 7 (or, = June+1)
CODE SECTION
Start:
mov eax, [today]
inc eax
mov [tomorrow], eax
....
This will assume your assembly files (and your own header files, if you wish) are in the GoAsm folder, along with GoLink.exe. The GoAsm manual describes the order in which the assembler looks for your included files, but if you put them in the GoAsm folder--you'll always be okay, although maybe a bit cluttered. When I have stuff in weird places I'll hard-code the location: #include "X:\movies\great movies\star trek.wav" (you wouldn't incl a wav file)
Usually, just copy that wav file to your working (let's say GoAsm) folder. You might mess it up or delete it by accident. Why lose the original?
In "some_other_assemfile.asm" you can put DATA, CODE, and CONST SECTIONS (and should) just like in the main file. Keep your assembly files short and focused for organization, named for what they do. I usually limit them to one main routine (plus obvious support stuff) and that's it. GoAsm is blisteringly fast, so don't worry AT ALL about lots of files and long compile times. It won't happen. If you use a tabbed multi-view editor like Notepad++ you can automate the whole thing, just like an IDE.
I just wrote this up quick. If someone spots something wrong, please point it out.
Bert
Some success. The compile worked.
The link failed........
Here are my latest changes:
MyProg.asm: #define LINKFILES, #define include.h (what I named Mr. Hansen's
header files), #include windows.h
MyProgbtch.bat: Set INCLUDE=g:\include.h, GoAsm /l/b/c MyProg.asm, GoLink
/unused "MyProg.obj"
Error message from GoLink: g:\>GoLink /unused "MyProg.obj" 'GoLink' is not
recognized as an internal or external command operable program or batch file
GoLink is in the GoAsm(g) folder. MyProg is now a folder in the GoAsm folder.
Thanks guys
in the batch file, specify the full path of GoAsm.exe and GoLink.exe
otherwise, you can add that folder to your PATH environment variable (similar to directions above)
the individual elements of the PATH variable are seperated by semicolons
you can add a folder to the PATH variable temporarily in the batch file with:
SET PATH=G:\GoAsm;%PATH%
that adds the G:\GoAsm folder to the already existing list of other folders that you have
when you modify an environment variable in a batch file, it is only valid for the life of the console window
Also, depending on what tools you have on your path and how many tool names are duplicated, you may have to control the order of the directories so that the correct tool will be found first.
MichaelW,
I've read your posts before and have learned from you. Thank you. One of the names I trust. Please explain what you mean to shankle in ordinary terms. I have no idea what you are saying, and only sort of at that, so this guy is brand new and really wants to learn this stuff. Why not be clear? Let's make this a big tent. It wasn't too long ago I came here looking for the next step after 65816ville and you guys helped me over and over again. Now I "get" it. Learning GoAsm is not easy. Masm is the popular and a "just perfectly fine" assembler, but we are here because GoAsm is literally the best assembler anyone has ever written. Let's help shankle--and every other person with the guts to program in assembler. And maybe, just maybe--someday let's all collaborate on a project together...
shankle,
Before I start this "whatever you call it" I want to strongly emphasize you read the GoAsm manual again. And again. Really. Jeremy wrote this effing excellent assembler, and he explains it well in the manual, but his coding examples will probably seem very foreign to you. He uses a lot of absolute references to places in memory, using numbers (e.g. +8) instead of labels, especially in the stack. He references memory in a very simple fashion--sort of old school. If you understand 6502, the x86 stack is about the same--but much larger in terms of its use. 256 bytes ($0100-$01FF) for a stack is nothin' today.
What Dave is saying is to create an Environment variable. His advice is good. If you do that you won't have to do the repetitive task of including the "set" line in your .bat file. It's true that the "set" line is only good for the life of the console. Well, that's the life of your programming session-normally. Now, you can do that, and you'll be fine, but what if, and I am testament to this, you change your entire scheme between "now" and "then"? Your programming playground may change. You may want a clean, new place to work. You might begin new work on drive "X." An environment variable is in the system. It's automated. It doesn't change. But---you change.
So what do you do?
This is the reason why I like the idea of a .bat file. When you type "myprog.bat" in the command line you transfer control to a file you wrote that assembles and links your program. It simplifies the mystical process of compiling your program into an .exe. I gave you a working outline in an earlier post. It's all about the current (working) directory, and GoAsm follows that premise. Your .bat file is in the GoAsm folder, as is GoAsm itself (and your files), so the compile process works from there.
Now, why would you name a folder "include.h"? That's a name for a file. You are a programmer, right? Give your folders a proper name--minus an extension. GoAsm looks first in the current folder, and then scours the drive(s) based on a logical formula (defined in the manual) for your files. Remember my batch file example? Try this layout:
G:
\GoAsm
GoAsm
GoLink
your assembly files
your personal .h files
your data files
the star trek .wav file
GoAsm\Help
...all your help files, including .pdf. chm, hlp, txt, doc (and anything else that might help you while programming)
GoAsm\include
Donkey's (Mr. Hansen's) files
other stuff you accumulate, such as number conversions, etc.
Relax. Seriously. I went through this--it was a tough nut to crack--and I've been breathing ASM since like 1975. I don't know--before 8-bit floating point. You'll get this and you'll be better for it. Get your directories in order--right now. They're fugged up (seems to me). Go simpler.
All assy programmers are brothers...
Hi Satpro,
Thanks for staying with me. I'm just about to give up on GoAsm. A 15 second compile and link
has taken me several days and I am no closer than on the 1st day.
Include.H is Mr. Hansen's entire header file. Ok I can see the value of renaming it and
I will. Yes, I think I have had my directories/folders all messed up.
I like the idea of a bat file where all the goodies of one program are kept.
Of course I have become lazy using MASM32. Never have to go through all this.
I have read the manuals and understand quite a bit of it. But I need more examples
to get me started. Once over this hurdle I will test MyProg-32 bit and see if that works.
Then I have to covert it to 64-bit GoAsm. If I get that far.........
:biggrin:
hang in there, Jack :t
satpro...
what Michael is talking about does not seem to apply to Jack's current dilema
but - let's say you have 2 different programs named shuffle.exe
one is in folder A and the other is in folder B
both folders are in the PATH environment variable
the operating system will use the shuffle.exe program from the folder which appears first in the PATH
UNLESS - the current folder has a program named shuffle.exe, then it would be executed
but - it is possible, that you may have to re-organize the folders in the PATH variable so that the OS finds the one you want first
a similar problem may arise because of the extension type
DOS executed BAT files before COM, before EXE
so - if you had a shuffle.bat file and a shuffle.exe file in the same folder, the batch file would execute
later operating systems had an entry in win.ini to set the order
now, there is a PATHEXT environment variable that determines the order
however, in Jack's case, the OS is simply not finding the GoLink.exe file
this can be solved by locating the executable and adding the full path to the batch command
or it can be solved by making sure that folder is in the PATH variable
another option is to copy GoLink.exe to a folder that is in the PATH :P
State as of today 6-30-2012.
Not trying to link just compile the program.
MyProg.asm: #define LINKFILES, #define jpsdhrs, #include \windows.h
MyProgbtch.bat: Set INCLUDE=g:\jpsdhrs, Set PATH=g:\jpshdrs, Set PATH=g:\goasm.exe,
GoAsm /l/b/c MyProg.asm
This is what's in the g:\GoAsm directory/folder:
C99, GDIPlus, golink, Garejorg, gozips(all the saved GO Zips), JG, jpshdrs(Mr. Hansen's
Header files), cmdline.txt, forhelp.txt, GoAsm.chm, goasm.exe, MyProgbtch.bat,
MyProg.asm, cedt.cmd, cedt.exe, cedt.mac AND about 50 *.h files I moved to the
g:\GoAsm directory/folder. Couldn't get a clean compile without doing it. There
has to be a better way.
Think I have the folder set up correctly. Not sure of the batch or program settings.
Here is the ERROR message I got when running MyProgbtch.bat:
Error! The system could not make the OBJ/LST output file. OBJ file not made.
ok Jack
i think we have a handle on the problem :P
Set INCLUDE=g:\jpsdhrs
Set PATH=g:\jpshdrs
Set PATH=g:\goasm.exe
GoAsm /l/b/c MyProg.asm
when you use SET PATH, it replaces the old PATH variable
so - first, you set the PATH to one folder, then to another
also - you cannot set a file into the PATH - only folders
what you really want to do is add a folder to the PATH - not replace it entirely
you can do this by expanding the previous value for the PATH variable...
SET PATH=G:\jpshdrs;G:\goasm;%PATH%
i am not so sure that you really want the header files in the PATH, however
try this batch file...
Set INCLUDE=g:\jpsdhrs
Set PATH=g:\goasm;%PATH%
GoAsm /l/b/c MyProg.asm
i just realized you may have another problem
that is...
the batch file you use to assemble is not in the PATH
this is why you have to place your ASM file in the same folder as the batch file
personally, i would set it up something like this
names without an extension are folders
names with an extension are files
follow the indentation to get an idea of the hierarchy...
G:\
--- GoAsm
------- Asm (this is where you want to put your projects)
------- Bin (this is where the GoAsm EXE files go - AND the batch files used to assemble/link)
------- Help (this is where the CHM and HTML help files go)
------- Include (this is where the headers go, with all the sub-folders)
i would then modify the system-wide environment variables (My Computer/Properties/Advanced tab)
PATH - i would add G:\GoAsm\Bin to the current PATH variable
INCLUDE - i would create a variable and point it to G:\GoAsm\Include
Thanks Dave,
Not sure I understand all of it but will give it a whirl.
Wish me luck...
Status as of 7-1-2012.
MyProg.asm - #define LINKFILES, #define jpshdrs, #include g:\jpshdrs
MyProgbtch.bat - Set INCLUDE= g:\jpshdrs, Set PATH=g:\codejps,
GoAsm /l/b/c MyProg.asm
GoAsm folder (g) -
bin - golink, Gorcjorg, GoAsm.exe, MyProgbtch.bat
codejps - MyProg.asm
gozips - Crimson editor svn286m, cedt-286m-setup.exe,
GoAsm.zip, Gorcjorg.zip
helpjps - forhelp.txt, GoAsm.chm
jpshdrs - (hopefully correct now. Before I had sub folders. Now only jpshdrs)
files - cderr.h, cedt.cmd, cedt.exe, cedt.mac, cmdline.txt(empty)
I am afraid to mess with the environment variables Dave. It's different in Windows 7.
Can't it be made to work with the batch file?
Here is the ERROR message I get now: ERROR - Could not open assembler source
file (MyProg.asm). OBJ file not made.
Not trying to do the link as yet.
ok - let's take a safe approach :biggrin:
when you open a console window and type:
PATH
and hit enter...
it will show you a list of folders, seperated by semicolons
find one of the folders that is already in the path
then, use that folder for your batch file
that way, when you type the name of the batch file, the OS can find it
then, we can use this for a batch file...
Set INCLUDE=g:\jpsdhrs
Set PATH=g:\goasm;%PATH%
GoAsm /l/b/c MyProg.asm
Hi Dave,
Got the Compile to output an ".obj" file.
MyProgbtch.bat - Set include=g:\jpshdrs, Set PATH=g:\codejps, GoAsm /l/b/c MyProg.asm
Here is what I did to get it to work. There is most likely a better way.
I moved MyProgbtch.bat and GoAsm.exe from the bin folder to the codejps folder.
In DOS I changed the c drive to the g drive: then did a g:cd \codejps
Executed MyProbtch.bat from the codejps folder and it compiled MyProg.asm
and output the MyProg.obj file.
From all this I'm expecting a the same hassle with the link process.
Just as a side my 10 ft dish survived Debbie and is working flawlessly.
If we get another one heading my way I'm going to take the dish down
and move it into the garage.
Thanks Dave for your patience.
well - at least you know it can work :t
however, that sounds like a lot of typing to go through
i happened to think of another way...
you can create a desktop icon for the command prompt
the easiest way to do that is to right-click on the command prompt shortcut that is in the start menu
then click Copy
then right-click on the desktop and click Paste
that should copy the shortcut to the desktop
once you have a desktop shortcut for the command prompt,
you can modify it by right-clicking on it - then Properties
you can make it run a batch file for you on startup that will only apply to that shortcut
(i.e. it won't affect the command prompt shortcut that is in the start menu)
that batch file can be located in G:\GoAsm\Bin and can be something simple like this
@ECHO OFF
SET PATH=G:\GoAsm\Bin;%PATH%
SET INCLUDE=G:\GoAsm\Include
G:
CD \GoAsm\Asm
then you can put all your stuff back where it was
as an added touch, you can rename the desktop shortcut to "GoAsm Console" or something
Status as of 7-1-2012.
Made a temp batch file for linking: - Set INCLUDE=g:\jpshdrs, Set PATH=g:\codejps,
GoLink /unused Kernel32.dll, User32.dll, Gdi32.dll, winspool.drv MyProg.obj
This of course gave errors. I thought all the Set commands with "jpshdrs" made the
header files available to the program and the linker. Obviously not.
Here are a few of the 40 or so items not defined in the object file - cbsize, hinstance, hwnd,
ShowWindowA, UpdateWindowA, msg, TransLateMessage, msg.wParam, GlobalAllocA.
I changed every API in the program from EX: ShowWindow to ShowWindowA. Maybe that's
not necessary.
yah - you should be able to take any one of Edgar's examples and make it fly :t
Compiles correctly.
At link time I get 3 error messages that the items are not found in the compile
or link:
cbsize, CS_BYTEALIGNWINDOW, hInstance
The headers are there to be used......
CS_BYTEALIGNWINDOW is used in WNDCLASSEX
cbsize is used in DOCINFO(A)
hInstance is used in .code/start but it isn't defined prior to that....????
also in WNDPROC in a LOCAL hInst: HINSTANCE
maybe you can post the source ?
Getting there... try cbSize - I think you need more there to, as in DOCINFO.cbSize, or whatever you named your instance of that structure (ex. MyDocInfo.cbSize). As for hInstance, you could define that as a global data variable. Not sure why CS_BYTEALIGNWINDOW shows up as not found since you seem to have the header files sorted out.
My latest fumblings 7-3.
CS_BYTEALIGNWINDOW, FF_ROMAN, IDI_APPLICATION were solved by the following code:
Styleid db 'CS_BYTEALIGNWINDOW',0
ffr db ' FF_ROMAN',0
appid db 'IDI_APPLICATION',0
These were fixed in WNDCLASSEX for EX:
D[wc.style], addr styleid etc.
Appname db 'MyProg.exe',0
These formats of DOCINFOA don't work:
doci DOCINFOa, [cbsize], [ Appname], null, null,0
doci DOCINFOa, cbsize, Appname, null, null,0
doci DOCINFOa, <cbsize, Appname, null, null,0> ; this one worked in MASM32
All three gave an error message that doesn't make any sense:
Misplaced Comma:- , cbsize, Appname, null, null, 0
Thanks guys for the help.....
Hmmm, fumblings is accurate... the compile errors will go away, but not really "solved" since your wc.style is actually now an address (and that address now points to a string, instead of a value). A more proper way to do something like this would be:
mov D[wc.style], CS_BYTEALIGNWINDOW
doci DOCINFO <SIZEOF(DOCINFO), Appname, NULL, NULL, 0>
If this does not work for you, then your build process still has an issue with accessing the definitions in the header files. As for IDI_APPLICATION, that looks like an ID for an icon which should be defined in your resource RC file... that definition also needs to be in your ASM file (or in a common include file for the project).
IDI_APPLICATION is one of the standard windows icons
it should be defined in user32.h
my mistake - it should be defined in winuser.h
Hi Guys,
To wjr,
mov D[wc.style], CS_BYTEALIGNWINDOW gives a compile error.
That is why I changed it to D[wd.style], addr stylied.
IDI_APPLICATION is also in WNDCLASSEX and making a zero string made
the compile error go away. FF_ROMAN is used in "CREATEFONT" and a
a zero based string made that error go away. Whether this is correct or
not I do not know but it eliminated the compile errors.
There is no resource file used in the program.
In reference to: doci DOCINFO <sizeof(Docinfo), Appname, NULL, NULL,0>
gave this error: unexpected material <sizeof Docinfo), Appname, NULL, NULL, 0>
This also didn't work if I changed DOCINFO to DOCINFOA.
To Dave,
I think I have my headers defined correctly of which one is winuser.h but as stated
above it gives compile errors in WNDCLASSEX. Both CS_BYTESALIGNWINDOW and
IDI_APPLICATION are defined in winuser.h. Question is why isn't it being picked up?
typically, i use IDI_APPLICATION like this
INVOKE LoadCursor,NULL,IDI_APPLICATION
mov hIcon,eax ;optional
mov wc.hIcon,eax
mov wc.hIconSm,eax
that's MASM syntax, of course :P
it seems to me that LINKFILES should be defined, then windows.h should be included first
#define LINKFILES
#include windows.h
#include winuser.h
Hi Dave,
I ASSUMED that LINKFILES brought in all the header files.
I compiled with: #INCLUDE windows.h and #INCLUDE winuser.h.
That gave an error. So I took out #INCLUDE winuser.h and that gave
a clean compile.
If I insert IDI_APPLICATION and CS_BYTEALIGNWINDOW in WNDCLASSEX
the linker rejects it. Had to put back "applid" and "styleid" to get it to work.
Don't understand why this is not picked up from "windows.h"
I know this goes against what you suggested prior but that suggestion gave
errors. To get around the errors my directory/folder setup is as follows:
g:\codejps - this contains GoAsm, GoLink, and all the header files.
So since GoAsm and the header files are all in the same folder it should be
able to access them. But I've been wrong before :(
If the symbol LINKFILES is defined before including windows.h, the header files then use #dynamiclinkfile to place the names of system DLLs to import from into a "directive" section of the OBJ file for use by GoLink so that you do not need to specify these on the GoLink command line.
Including windows.h should be sufficient as it should include winuser.h and wingdi.h. However, the above DOCINFO unexpected material error (and FF_ROMAN) would seem to indicate that wingdi.h also is not getting included.
Yes, all in the same folder should work. If you do not get any "Could not open file:- #include FileName.h" errors, then perhaps a long shot, but make sure that you have not defined NOUSER and NOGDI before including windows.h. Also, although those other changes are incorrect, if there are no compile errors, what do you get, if any, as GoLink errors?
Thank you for replying WJR.
This is how LINKFILES is defined in MyProg:
#define LINKFILES
#include windows.h
#define codejps
DOCINFO and FF_ROMAN seem to be behaving themselves now.
I do not have "NOUSER OR NOGDI" in the program.
I have changed CS_BYTEALIGNWINDOW and IDI_APPLICATION back
to the way they should be.
I can get a clean compile.
The linker blesses me with this error:
The following symbols were not defined in the object file or files:-
CS_BYTEALIGNWINDOW IDI_APPLICATION
Quote from: shankle on July 05, 2012, 05:37:35 AM
This is how LINKFILES is defined in MyProg:
#define LINKFILES
#define windows.h
shouldn't that be
#define LINKFILES
#include windows.h
Hi Dave,
Looks like I can't get away with anything :biggrin:
Yes, will fix it.
Thanks
If WinUser.h was somehow not being included, then you would have other errors (for example with WNDCLASSEX, RegisterClassEx, and WM_CREATE). So if it is just those two, check to see if they actually get defined in your WinUser.h file.
Thank you WJR for responding.
In winuser.h I found:
#define IDI_APPLICATION 32512 and #define CS_BYTEALIGNWINDOW 0x2000
These switches are in winuser.h. Is it possible that winuser.h is not fully compatible
with Windows 7 Pro 32-bit?
My Wndclassex code:
mov D[wc.cbSize], SIZEOF WNDCLASSEX
mov D[wc.style], addr CS_BYTEALIGNWINDOW
mov D[wc.lpfnWndProc], OFFSET WndProc
mov D[wc.cbClsExtra], NULL
mov D[wc.cbWndExtra], NULL
push [hInst]
pop [wc.hInstance]
invoke LoadIconA, NULL,addr IDI_APPLICATION
mov D[wc.hIcon], eax
invoke LoadCursorA, NULL,[IDC_ARROW]
mov D[wc.hCursor], eax
invoke CreateSolidBrush, [colorbk] ; background color
mov D[hBrush], eax
mov D[wc.hbrBackground], eax
mov D[wc.lpszMenuName], NULL
mov D[wc.lpszClassName], OFFSET szDisplayName
mov D[wc.hIconSm], 0
LoadIcon IDI_APPLICATION is defined in winuser.h but gives an error
Loadcursor IDI_ARROW is NOT defined in winuser.h but gives NO error :(
mov D[wc.style], addr CS_BYTEALIGNWINDOW is defined in winuser.h but gives an error.
Switches used in winuser.h
WINNT4 = Used only with Windows NT version 4 (note: may not be compatible with other versions)
WIN9X = Used only with Windows 95/98/ME (note: may not be compatible with other versions)
WINCE = Used only with Windows CE (note: may not be compatible with other versions)
WIN64 = For use only with a 64 bit Windows
UNICODE = Use UNICODE versions
All the structs have been found but one.
In winuser.h the RECT struct is defined as follows:
#IFNDEF RECT STRUCT left dd ? top dd ? right dd ? bottom dd ? ENDS rc RECT
I had to code this in MY Progie to get rid of errors.
The header files have been around for a while on the 32-bit side, so I would say no compatibility issues. Ahhh, that would do it... you do seem to have the header files working now, so if you have difficulties that you can not resolve on your own, posting a bit source code will make things Go much quicker. Corrected lines follow:
mov D[wc.style], CS_BYTEALIGNWINDOW
invoke LoadIcon, NULL, IDI_APPLICATION
invoke LoadCursor, NULL, IDC_ARROW
Also, the header files take care of the ANSI / UNICODE API naming, so you usually do not need to specify the A version.
Thanks WJR,
The errors in the WNDCLASSEX struct are now history.
Still have the RECT struct coded in the program.
Without it I get errors.
Working on some other link errors which I am trying to solve.
Down to 3 errors at link time.
rc, dwvalue and hInst
RC with RECT
dwValue with dwtoa
I have the RECT struct defined in the program and it is giving an error
RC.dll not found. It doesn't seem to call any headers.
If I take the RECT Struct of the program I get errors.
Quote from: shankle on July 09, 2012, 10:32:42 PM
Down to 3 errors at link time.
rc, dwvalue and hInst
RC with RECT
dwValue with dwtoa
dwvalue is not the same as dwValue :P
RC.dll may be the resource compiler - not the RECT struct
WJR = Wayne J. Radburn :t
Let the header files define RECT. There would appear to be an "RC.dll" in your source (not the same as rc.dll), but dll is not a member of RECT, so that should be easy to find and fix.
To Dave,
Yep - a typo - dwValue
To WJR,
I let the header files reference RECT.
I have included in the program "windows.h and Mr. Hansen's header files".
There is NO reference in the program to "RC.DLL".
Here is the error I am getting at link time:
Could not open an input file (RCC.DLL) needed for forced dll or import
by ordinal.
Thanks fellows
Assuming you are simply trying to use a RECT called RC, the "forced dll" part would lead me to believe that in your source you have a reference to "RC:" (or "RCC:") with a colon (where one is not needed, or instead of a period).
Got a clean compile and link.
When I click on MyProg.exe it blinks and nothing happens.
Major digging now. Have a test msg in WM_PRINT and it never gets there.
Will put a test msg in WM_CREATE to see if it gets there.
Seems I am confused in GoAsm as to when to put in a Frame/Local
statement for ex: rc:RECT or rc?
Will do more digging to see if I can figure this one out.
Local msg, MSG, hWnd, hBrush, rc, wc_WNDCLASSEXA, cc:codecode
that's a bit of a mess, Jack :biggrin:
local's need to be typed
MSG is a structure (which is a sort of a type)
try this
LOCAL msg:MSG, hWnd:HWND, hBrush:HBRUSH, wc:WNDCLASSEX, cc:codecode
i am not sure about the last one
i assume you have defined the type (or structure) "codecode" somewhere previously in the source
MSG and WNDCLASSEX are structures that should be defined in the include files
HWND and HBRUSH are DWORD types that should be defined in the include files
In the testing phase of MyProg.exe I got this error message:
" MyProg.exe has stopped working"
No 1st screen or anything. I put messagebox code right after the START
and it did not execute. So I assume something else is happening.
.code
start:
; test begin
;TM3 db 'at start',0
invoke MessageBox, NULL,[TM3],[TM3],MB_YESNO
cmp eax,MB_YESNO
je > Twrite
Twrite:
; test end
invoke GetModuleHandleA, NULL
mov [hInstance],eax
invoke GetCommandLine
invoke WinMain
invoke ExitProcess,eax
WinMain:
FRAME hInst,hPrevInst,CmdLine,CmdShow,hInstance;
LOCAL msg,MSG,hWnd,hBrush,rc:RECT,wc:WNDCLASSEXA;,cc:codecode
mov D[wc.cbSize], SIZEOF WNDCLASSEXA
mov D[wc.style], CS_BYTEALIGNWINDOW
mov D[wc.lpfnWndProc], OFFSET WndProc
mov D[wc.cbClsExtra], NULL
mov D[wc.cbWndExtra], NULL
push [hInst]
pop [wc.hInstance]
invoke LoadIcon, NULL,IDI_APPLICATION
mov D[wc.hIcon], eax
invoke LoadCursor, NULL,IDC_ARROW
mov D[wc.hCursor], eax
invoke CreateSolidBrush, [colorbk] ; background color
mov D[hBrush], eax
mov D[wc.hbrBackground], eax
mov D[wc.lpszMenuName], NULL
mov D[wc.lpszClassName], OFFSET szDisplayName
mov D[wc.hIconSm], 0
invoke RegisterClassExA, addr wc
invoke SystemParametersInfoA, SPI_GETWORKAREA,0,addr rc,0
mov eax,[rc.left]
mov [holdleft], eax
mov eax,[rc.right]
mov [holdright],eax
mov eax,[rc.bottom]
mov [holdbottom],eax
mov eax,[rc.top]
mov [holdtop],eax
push edx
push ebx
xor edx,edx
mov eax,[holdright]
mov ebx,2
div ebx
mov [savemiddleofX],eax
pop ebx
pop edx
INVOKE CreateWindowExA, NULL,addr szDisplayName,addr AppName,\
WS_OVERLAPPEDWINDOW,[rc.left],[rc.top],[rc.right],\
[rc.bottom],NULL,NULL,[hInst],NULL
mov [hWnd],eax
INVOKE ShowWindow, [hWnd], SW_SHOWNORMAL
INVOKE UpdateWindow, [hWnd]
.StartLoop
INVOKE GetMessageA, addr msg,NULL,0,0
cmp eax,0
je >.ExitLoop
INVOKE TranslateMessage, addr MSG
INVOKE DispatchMessageA, addr MSG
jmp .StartLoop
.ExitLoop
mov eax,[MSG.wParam]
ret
ENDF
try this program, Jack
.data
TM3 db 'at start',0
.code
start:
invoke MessageBox, NULL,TM3,TM3,MB_YESNO
invoke ExitProcess,0
end start
GoAsm syntax differs a bit from MASM for an address:
INVOKE MessageBox, NULL, ADDR TM3, ADDR TM3, MB_YESNO
Going further, you will need Dave's above corrections for LOCAL and the
WinMain FRAME should have 4 parameters:
WinMain:
FRAME hInst, hPrevInst, CmdLine, CmdShow
More importantly, when you call WinMain, you need to supply those arguments to the call, something like:
INVOKE GetCommandLine
mov [pCmdLine],eax
INVOKE WinMain, [hInstance], NULL, [pCmdLine], SW_SHOWDEFAULT
Also, in your message loop, change MSG (structure template) to msg (your locally defined instance of that structure).
hi Wayne
i thought GoAsm syntax would yield the address of TM3 for "TM3"
and the contents at the address for "[TM3]"
Some progress.
I did get my info msg at START. Then moved it to WinMain and nothing.
To Dave- codecode is a struct I defined, TM3 is a msg defined in the data section.
To WJR - I changed to LOCAL statement to the following:
Local msg:MSG, hWnd:HWND, hBrush:HBRUSH, rc:RECT, wc:WNDCLASSEX, cc:codecode
Now I get the following error: "data type given in local data declaration not recognized:-
HWND"
I changed MSG to msg as directed in your last statement.
Here is the error msg I get: "The following symbol was not defined in the object file
or files:- msg.wParam"
Guess I'm confusing the Local statement with the Frame statement?
if HWND is not defined in the include files, it probably should be
if one of the DWORD typedef's is missing, you can just use DWORD...
Local hWnd:DWORD
however, it may be that the name "hWnd" is used elsewhere in a visible scope
it might be easier if you'd attach the source, then Wayne or i could step through and clean it up in one pass
QuoteI changed MSG to msg as directed in your last statement.
Here is the error msg I get: "The following symbol was not defined in the object file
or files:- msg.wParam"
this problem may be related to the first
if it doesn't resolve all of the LOCAL declarations, it may not create any of them
Correction, the syntax difference is more on the memory addressing side which requires the square brackets for the contents at the address. For the address itself though, ADDR or OFFSET (either one for GoAsm, some differences between them for MASM) is still required in code (but not necessary as part of a data declaration).
A very quick look and I didn't see HWND defined in the header files, in WinDef.h at least, so you can also use HANDLE instead. Not sure why msg does not work for you if rc and wc do...
Thanks guys.
Made some progress and am now into the WM_CREATE.
invoke GlobalLock, eax
mov [pinf04],eax t
mov ebx,eax
push ebx
invoke EnumPrintersA, PRINTER_ENUM_LOCAL,NULL,4,[pinf04],\
[dwNeeded],[dwNeeded],[dwReturned]
pop ebx
invoke CreateDC, NULL,[ebx+PRINTER_INFO_4.pPrinterName],NULL,NULL
The offending instruction is the CreateDC. The ones above it worked.
Most likely it is the ebx+PRINTER_INFO_4.pPrinterName
see if this works
invoke GlobalLock, eax
mov [pinf04],eax t
mov ebx,eax
invoke EnumPrintersA, PRINTER_ENUM_LOCAL,NULL,4,eax,\
[dwNeeded],[dwNeeded],[dwReturned]
lea eax,[ebx+PRINTER_INFO_4.pPrinterName]
invoke CreateDC, NULL,eax,NULL,NULL
Hi Dave.
The lea seemed to solve the problem. More testing will tell for sure
Masm can do the lea for you:
invoke CreateDC, NULL, addr [ebx+PRINTER_INFO_4.pPrinterName], NULL, NULL
Hi JJ2007 and thanks for the reply.
GoAsm gives an error with your suggestion.
Now the ShowWindow gives an error in GoAsm.
It works fine in Masm32.
The CreateWindowEXA works.
hWnd dd 0
INVOKE CreateWindowExA, NULL,addr szDisplayName,addr AppName,\
WS_OVERLAPPEDWINDOW,[rc.left],[rc.top],[rc.right],\
[rc.bottom],NULL,NULL,[hInst],NULL
mov [hWnd],eax
INVOKE ShowWindow, [hWnd], SW_SHOWNORMAL
INVOKE UpdateWindow, [hWnd]
Sorry, as you most likely found out, LOCAL hWnd:DWORD and LOCAL hWnd:HANDLE do not work. The corrections for these would be LOCAL hWnd:D and LOCAL hWnd:%HANDLE.
However, since for a 32-bit application a DWORD is the default, this can be simplified to LOCAL hWnd which is better if you plan on going to 64-bit where QWORD is the default (the %HANDLE definition in the header files would also make this switch).
If you get an error, please specify the GoAsm or GoLink error message. I do not get an error using what you originally had for your CreateDC call. If you used a global variable for hWnd, I do not see how that would give an error. Given that you have had some problems with hInst, although CreateWindowExA works, does it return NULL? This is an error condition that you should generally check for, since ShowWindow and the rest are not going to work.
In my test the CreateWindowEX tests as BAD.
This is the same code I use in my MASM32 version.
Regardless of the test the CreateWindowEX does not work.
No error message from GoAsm but a Window message saying the program
has stopped working. cc:codecode is a struct I defined.
WinMain:
FRAME hInst,hPrevInst,CmdLine,CmdShow
LOCAL msg:MSG,rc:RECT,wc:WNDCLASSEXA,cc:codecode,hWnd
; more code which is already mentioned in a prior mesage
INVOKE CreateWindowEx, NULL,addr szDisplayName,addr AppName,\
WS_OVERLAPPEDWINDOW,[rc.left],[rc.top],[rc.right],\
[rc.bottom],NULL,NULL,[hInst],NULL
mov [hWnd],eax
;test code begin
;szText db ' executed correctly',0
;szText2 db ' is bad',0
;szCaption db 'TRUE',0
;szCaption2 db 'FALSE',0
pusha
cmp D[eax], 0 ; success
je >
invoke MessageBox, NULL, ADDR szText, ADDR szCaption, MB_OK
jmp >out1
:
invoke MessageBox, NULL, addr szText2, addr szCaption2, MB_OK
out1:
popa
;test code end
INVOKE ShowWindow, [hWnd], SW_SHOWNORMAL
INVOKE UpdateWindow, [hWnd]
; message loop here also mentioned in a prior message
rc.right and rc.bottom are screen coordinates
the parameters should be width and height
another potential problem...
the window handle is generally stored in a global variable, rather than a local one
this may not be a problem, if the code is written to always use a local value
Hi Dave,
The rc.bottom/rc.right are part of the RECT struct.
This worked fine in my MASM32 program.
If anything I would suspect the trouble is caused by how I have defined hWnd.
As far as I know I am using a Local definition of hWnd.
This also worked in the MASM32 version.
In answer to WJR the CreateWindowEX does return 0/NULL .
Question is why and what caused it?
what value does the WM_CREATE code in WndProc return in EAX ???
WM_CREATE should return 0 to continue creation of the window
if WM_CREATE returns -1, window creation ceases and CreateWindowEx returns 0
also - the message loop must be working properly for WM_CREATE to succeed
at any rate - you can use GetLastError for a general idea of what caused the problem
i am guessing - invalid parameter :P
or maybe - class not found
also...
window creation causes a myriad of other messages to be sent to WndProc
like getminmaxinfo, paint, ncpaint, geticon - a bunch of em
be sure the structure of WndProc allows unhandled messages to pass to DefWindowProc :t
The local hWnd looks good now. Before diving into other messages, to be on the safe side, did you fix your INVOKE WinMain as suggested back in Reply #65? If not, your value in [hInst] will be wrong causing CreateWindowEx to fail.
I did make the changes requested as seen below:
pCmdline dd 0
invoke GetModuleHandleA, NULL
mov [hInstance],eax
invoke GetCommandLine
mov [pCmdline], eax
invoke WinMain, [hInstance],NULL,[pCmdline],SW_SHOWDEFAULT
invoke ExitProcess,eax
WinMain:
FRAME hInst,hPrevInst,CmdLine,CmdShow
LOCAL msg:MSG,rc:RECT,wc:WNDCLASSEXA,cc:codecode,hWnd
pusha
;
;
;
popa
PUSHA and POPA push and pop words !!!
use PUSHAD and POPAD to push and pop dword register values
INVOKE CreateWindowEx, NULL,addr szDisplayName,addr AppName,\
WS_OVERLAPPEDWINDOW,[rc.left],[rc.top],[rc.right],\
[rc.bottom],NULL,NULL,[hInst],NULL
mov [hWnd],eax
.if !eax
invoke GetLastError
invoke MessageBox,0,uhex$(eax),0,0
invoke ExitProcess,0
.endif
this code will display the error as a string, rather than a number :biggrin:
INVOKE CreateWindowEx, NULL,addr szDisplayName,addr AppName,\
WS_OVERLAPPEDWINDOW,[rc.left],[rc.top],[rc.right],\
[rc.bottom],NULL,NULL,[hInst],NULL
mov [hWnd],eax
.if !eax
INVOKE GetLastError
sub esp,512
xor ecx,ecx
mov edx,esp
INVOKE FormatMessage,FORMAT_MESSAGE_FROM_SYSTEM,ecx,eax,ecx,edx,512,ecx
mov edx,esp
xor ecx,ecx
INVOKE MessageBox,ecx,edx,ecx,ecx
add esp,512
INVOKE ExitProcess,0
.endif
Hi Dave,
The "IF" statement in your last message has to be changed to GoAsm format.
I have no idea how to change "!eax" to GoAsm format. I interpret it as not eax.
Otherwise I would try it.
.if !eax means "if not eax" or "if eax is 0"
INVOKE CreateWindowEx, NULL,addr szDisplayName,addr AppName,\
WS_OVERLAPPEDWINDOW,[rc.left],[rc.top],[rc.right],\
[rc.bottom],NULL,NULL,[hInst],NULL
mov [hWnd],eax
or eax,eax
jnz around
INVOKE GetLastError
sub esp,512
xor ecx,ecx
mov edx,esp
INVOKE FormatMessage,FORMAT_MESSAGE_FROM_SYSTEM,ecx,eax,ecx,edx,512,ecx
mov edx,esp
xor ecx,ecx
INVOKE MessageBox,ecx,edx,ecx,ecx
add esp,512
INVOKE ExitProcess,0
around:
Hi Dave,
Added your code with the small change to conform to GoAsm format.
cmp D[eax],0
jnz > around
Maybe this sequence might shed some light on the problem.
I have message boxes at Start, just before CreatWindowEX in winmain and
at the end of WM_CREATE. I have Dave's error code just after the CREATEWINDOWEX.
When I run the program these messagebox messages occur in this order:
Start, WinMain, WM_CREATE, BOMB. The program is supposed to return to
CreatWindowEX after WM_CREATE. Then Dave's error code check would execute
if an error occurred. Instead a windows message occurs stating that the program
has stopped working.
QuoteInstead a windows message occurs stating that the program has stopped working.
i hope it says more than that :P
i would have to guess there is a problem with WndProc
but - it could be the message loop, as well
make sure WndProc is set up correctly, in general:
EBX, EBP, ESI, EDI are preserved (or not destroyed)
the routine pops 4 dword parms when it returns (RET 16)
unhandled messages invoke DefWindowProc and return it's value from EAX
you could put a temporary version of WndProc in there that handles a minimal number of messages
for example - if it just handles WM_CLOSE and WM_DESTROY
at least you can see if a window appears
in fact, all you need is DefWindowProc and a RET (close is nice, though)
INVOKE DefWindowProc,hWnd,uMsg,wParam,lParam
ret
you might disassemble the code
sometimes, the problem glares at you from the disassembled code, while the source format hides it
attach the EXE to your next post - i'll have a look
Correct me if I'm wrong, but you need to handle WM_DESTROY with a PostQuitMessage, no? Without it the window will close, but the process will remain running. DefWindowProc does not do it.
yes - that's what i say
but - you can assemble and run without it - then use the task manager to close it
the idea here is a one-time temporary test to see if things are put together right
; MASM32 This code is working in MASM32
WndProc PROC hWnd:HWND, iMsg:UINT, wParam:WPARAM, lParam:LPARAM
mov eax,iMsg
.IF eax==WM_CREATE
;-------------------------
; This code does not work in GoAsm
WndProc FRAME hWnd,iMsg,wParam,lParam
; test begin
;TM8 db 'begin of wndproc',0
pushad
invoke MessageBox, NULL,addr TM8,addr TM8,MB_YESNO
popad
; test end
mov eax,[iMsg]
uses ebx,edi,esi
Local hMemory,stm:SYSTEMTIME,ps,hdc,hdcPrn
;.WM_CREATE
cmp D[eax],WM_CREATE
jne >>.WM_CHAR
; ----------------------
cmp D[eax],WM_CHAR
jne >>.WM_PAINT
;--------------------------
.WM_PAINT
cmp D[eax],WM_PAINT
jne >>.WM_DESTROY
;------------------------------
.WM_DESTROY
cmp D[eax],WM_DESTROY
jne >
What happens is it never gets to WM_Create.
Then gives windows error message: MyProg has stopped working.
If I take my test message out then it gets to WM_create and then gives
a Windows error message : MyProg has stopped working.
If I change cmp D[eax],WM_CREATE to cmp D[iMsg], WM_Create then
it goes into a loop in Wndproc. Have to power down the puter to get out if it.
Getting there... the code for your procedure should be placed after USES and LOCAL (not sure if your ps should be ps:PAINTSTRUCT). Once you have the value of the message in eax, for the comparisons use eax and not D[eax].
WndProc FRAME hWnd,iMsg,wParam,lParam
USES ebx,edi,esi
LOCAL hMemory,stm:SYSTEMTIME,ps,hdc,hdcPrn
mov eax,[iMsg]
.WM_CREATE
cmp eax,WM_CREATE
jne >>.WM_CHAR
When you tried cmp D[iMsg], WM_CREATE that was actually correct. Unfortunately, that means there are still some corrections after this point to track down...
shankle,
I noticed a few things. GoAsm doesn't use if/then/else syntax. It is only for conditional assembly. Using a combination of compares and/or testing flags is the way you have to go. Anyway, it'll keep you much more focused on the state of the machine. Knowing the zero, carry, and sign flags inside-out is a must. Also, please read the GoAsm manual section on stack frames/local variables. There is a specific order, e.g. FRAME, USES, LOCAL, CODE,RET,ENDF / ENDU.
I tried to see if I could fix your WndProc--it was just too much for me--sorry. It needs to be simpler--much simpler. So, I've included the bare bones of one I use. It is also originally from the manual. I hope I didn't rip too much out, but it should basically work for you. The "message compares" use a table with the messages and the addresses of the message handler routines. If you want to be good at this you'll start using tables for much of what you do, especially math. Tables are magnitudes faster than anything else because you can branch on flags--and they will always work better than a bunch of slow, sloppy compares that you find in if/then statements. Sometimes (multiplication) tables can be big. But so what? Always better than slow. And you'll hear guys argue against that point. They probably didn't program 1 mHz 8-bit computers in the 70s & 80s, when every byte (and every cycle) mattered. Good assembly language is an art, no doubt about it. That stuff still matters. So, be an artist. Let the "C" guys rely on the fact they have (and need) a 3 gHz machine to make complicated stuff fly.
About saving registers: if you don't modify them in a procedure don't bother to push/pop them. Blindly pushing all the registers makes for slow code. Eventually you'll even start to notice the difference in execution speed. But, if you "use" them, then by all means you should "USES" them. That's the good thing about "USES". You just look through that particular procedure and code a "USES reg,reg,reg" etc for each register used IN THAT PROCEDURE. The exception is if you return something in, say eax. You'll wind up losing it if you "USES" it ;) . (Did that on purpose)
So, I hope you can use this. Study how that message table works. It's old-school, quality assembly--and it's FAST. And simple. It's Jeremy Gordon code...
CONST SECTION
; ******************** Window message table **********************
;
MESSAGES DD WM_CREATE, CREATE ;the message then the code address
DD WM_DESTROY, DESTROY
DD WM_PAINT, PAINT
DD WM_KEYDOWN, KEYDOWN
DD WM_TIMER, TIMER
CODE SECTION
WndProc:
FRAME hwnd, Msg, wParam, lParam ;establish stack frame, get params
;--------------------------------------
;FIND THE A MESSAGE WE'RE INTERESTED IN
;--------------------------------------
mov eax, [Msg] ;get in eax message sent by Windows
mov ecx, SIZEOF MESSAGES/8 ;get number of messages to look at
mov edx, ADDR MESSAGES
P1:
dec ecx ;count down to -1
js >.notfound ;if = -1 then call default win procedure
cmp [edx+ecx*8],eax ;check table if entry is correct message
jnz <P1 ;no, so loop back for next table entry
call [edx+ecx*8+4] ;call the correct procedure for the message
jnc >.exit
;----------------------------------------
;NO MATCH, SO PASS PARAMS BACK TO WINDOWS
;USE DEFAULT WINDOWS PROCEDURE
;----------------------------------------
.notfound
push [lParam]
push [wParam]
push [Msg]
push [hwnd]
call DefWindowProc
;------------------------------
;DESTROY STACK FRAME, EXIT BACK TO WINDOWS HERE
;------------------------------
.exit
ret
ENDF
;_________________________________________________________________________
;_________________________________________________________________________
PAINT: ; RE-DRAW WINDOW
USEDATA WndProc ;use parameters sent to WndProc
USES ebx,ecx,edx,esi,edi ;only save registers that you use in this procedure
;-------------------------------------------------------------
;---------------
;CALL BEGINPAINT
;---------------
invoke BeginPaint, [hwnd], ADDR ps
mov [hdc], eax
;updating/refreshing the window goes here
;-------------
;CALL ENDPAINT
;-------------
invoke EndPaint, [hwnd], ADDR ps
;---------
;EXIT HERE
;---------
xor eax,eax ;return not carry and eax = 0
ret
ENDU ;finished using parameters sent to WndProc
;_________________________________________________________________________
;_________________________________________________________________________
CREATE: ; PROGRAM INITIALIZATION
USEDATA WndProc ;use parameters sent to WndProc
USES ebx,ecx,edx,esi,edi ;only save registers that you use in this procedure
;-------------------------------------------------------------
;program init goes here
;---------
;EXIT HERE
;---------
xor eax,eax ;return not carry and eax = 0
ret
ENDU ;finished using parameters sent to WndProc
;_________________________________________________________________________
;_________________________________________________________________________
DESTROY: ; PROGRAM CLEAN-UP
USEDATA WndProc ;use parameters sent to WndProc
USES ebx,ecx,edx,esi,edi ;only save registers that you use in this procedure
;-------------------------------------------------------------
invoke PostQuitMessage, NULL ;kill the application
;---------
;EXIT HERE
;---------
xor eax,eax ;return not carry and eax = 0
ret
ENDU ;finished using parameters sent to WndProc
;_________________________________________________________________________
;_________________________________________________________________________
KEYDOWN: ; PROCESS KEYPRESSES
USEDATA WndProc ;use parameters sent to WndProc
USES ebx,ecx,edx,esi,edi ;only save registers that you use in this procedure
;-------------------------------------------------------------
mov eax, [wParam]
;----------------------
;COMPARE TO ESCAPE KEY?
;----------------------
cmp eax, VK_ESCAPE
jne >E9
invoke PostQuitMessage, NULL ;kill the application
;---------
;EXIT HERE
;---------
E9:
xor eax,eax ;return not carry and eax = 0
ret
ENDU ;finished using parameters sent to WndProc
;_________________________________________________________________________
;_________________________________________________________________________
TIMER: ; PROCESS TIMERS
USEDATA WndProc ;use parameters sent to WndProc
USES ebx,ecx,edx,esi,edi ;only save registers that you use in this procedure
;-------------------------------------------------------------
;code if you process timers
;I always use timer proc outside the window
;---------
;EXIT HERE
;---------
xor eax,eax ;return not carry and eax = 0
ret
ENDU ;finished using parameters sent to WndProc
Quote from: satpro on July 14, 2012, 12:54:06 PM
Blindly pushing all the registers makes for slow code.
Sounds familiar (http://masm32.com/board/index.php?topic=250.msg1314#msg1314) :biggrin:
yah - but you're bloating CPU time that might be used for some other process, nonetheless
Quote from: jj2007 on July 14, 2012, 06:24:46 PM
Quote from: satpro on July 14, 2012, 12:54:06 PM
Blindly pushing all the registers makes for slow code.
Sounds familiar (http://masm32.com/board/index.php?topic=250.msg1314#msg1314) :biggrin:
And it wasn't that long ago, was it? I realized (while) you all were having that discussion the answer was basically rule #1 in assembly: "Preserve that and only that which you use." It's actually quite simple, and the same whether we're talking 65x, 68x, or x86. At the time I thought there might be some hard and fast rule among the hard-cores here, so I decided to ask. Turns out there wasn't. My simple question turned into quite a lengthy discussion, didn't it?
There is no need for a lengthy discussion, there is a need for empiric evidence instead of religious beliefs. Tell me by how much % a "uses esi edi ebx" slows down your proggie, and I'll keep my mouth shut.
Well, there was a lengthy discussion about the topic, and quite a few of us were part of it. If you add six (or more) needless cycles (and bytes) to every procedure, especially the Window Procedure, running over and over, it's not just a percentage--it's a hard count. That stuff adds up. Maybe some guy is still using an old laptop with 1 cpu @ 800 mHz, trying to play music in the background. That style of programming will cost HIM up to 16x. You don't think he'll notice?
So, save those registers for no reason. Freedom of expression--I don't imagine anyone will stand in your way. But if we are talking assembly language technique (religious belief?) then it's a different story, especially for someone like shankle who might be new to this and only wants to learn good, solid programming. Yes, of course it's safe to push ebx, esi, and edi, or to push all of them for that matter. It's also borderline sloppy. Why do it if it's not necessary?
I just hope it's not because "... otherwise Hutch & me & a few others will eat you alive :greensml: ." ->Incidentally, Hutch excluded his name from that line...he put it this way:
" :biggrin:
This is actually BAD advice."
Quote from: satpro on July 15, 2012, 07:31:18 AMit's not just a percentage--it's a hard count.
Show us. Express it in nanoseconds, cycles, percent, whatever, but quantify the "hard count". Show us how much the guy with the 800 MHz CPU will suffer.
(no need to indulge in precision, a back-of-the-envelope formula will be sufficient)
I made my point.
But ok... against today's 4x cpu, 3.2 gHz machine. BTW, how does one expect nanoseconds and cycles, yet precision is not necessary?
Quote from: satpro on July 15, 2012, 07:45:03 AMBTW, how does one expect nanoseconds and cycles, yet precision is not necessary?
jj: (http://masm32.com/board/index.php?topic=250.msg1314#msg1314) "Most [messages] are WM_MOUSEMOVE - a particularly fast one with only 600 cycles. 3 push/pop pairs delay WM_MOUSEMOVE by 0.00008 milliseconds - how significant is that delay?"
That is admittedly not very precise, it could be 0.00007 milliseconds or 0.00009 milliseconds, but it still holds much more information than a bold "unnecessary pushing costs a lot of CPU power" statement. Remember your CPU does approx. 2500000000 cycles per second. If you want to get it sweating, you need something much more serious than a little pushin' and poppin' in an idle WndProc, e.g.
xor ecx, ecx
@@: dec ecx
jne @B
is a good exercise for your lazy CPU :bgrin:
I understand that you like the idea of being a hardcore assembly programmer, but what you claim is simply not relevant in a WndProc, because a) these messages don't get called a Million times per second and b) the extra 3 cycles are statistical noise when def-processing the message takes many hundreds of cycles.
Your line of reasoning does hold in an innermost loop which gets called a Million times (and yes, I mean a Million, not less). And of course, you would never call a subproc from within an innermost loop, so the push & pop argument is completely irrelevant anyway.
jj,
I get where you're coming from, and for the most part assembly corrects all of C/C++s speed issues, but I really want to get back to shankle's problems learning GoAsm. He deserves a fair shot at this. I think I was responding initially to the sarcasm aimed at me regarding the question I asked a couple of weeks ago just looking for an informed opinion about register preservation. I'm not some newbie--quite the opposite. Just never really heard anyone talking about it before. So I asked.
If you want to make the cpu sweat, simply switch over to PeekMessage. That'll do it. As an aside, I still have one of those 800 mHz laptops that I use when I'm away from this thing and my wife's is in her hands. That machine sweats everything--except for well-written assembly programs like qeditor or GoAsm. I can literally see mouse activity in the task manager. So yes, I do believe everything is relevant, that's all. Hardcore? Probably. But I'm okay if you have a different programming style. We all do.
Quote from: satpro on July 15, 2012, 09:09:52 AMI can literally see mouse activity in the task manager.
Hardcore assembler coders
measure it, so give us figures: Is this CPU weak enough to turn an extra 0.00008 milliseconds per WM_MOUSEMOVE into "activity" in the task manager?
So go and test it with one of the standard Windows apps that you find in \masm32\examples, then we have something real to discuss.
Just in case your task manager doesn't notice your frenetic attempts: A WM_MOUSEMOVE per se needs 600 cycles, plus 3 for a little push'n and popp'n. But there are apps out there in the evil Microsoft & Adobe etc world which do,
in the WM_MOUSEMOVE handler, incredibly slow things that may take ten-thousands of cycles... and that rubbish will indeed show as "activity" in the task manager".
This is not about my main problem but am curious why this messagebox is causing problems.
This code causes the messagebox to go into a loop from which I have to
reboot the puter to get out of it. It never goes to WM_CREATE.
WndProc:
FRAME hWnd,iMsg,wParam,lParam
USES ebp,ebx,edi,esi
Local hMemory,stm:SYSTEMTIME,ps:PAINTSTRUCT,hdc,hdcPrn
; USED ONLY DURING TESTING PHASE
;TM8 db 'begin of wndproc',0
pushad
invoke MessageBox, NULL,addr TM8,NULL,MB_OK
popad
; test end
;.WM_CREATE
cmp D[iMsg],WM_CREATE
jne >>.WM_CHAR
;.........................................
This code does execute WM_CREATE and then goes back to my original problem
which I'm still trying to resolve.
WndProc:
FRAME hWnd,iMsg,wParam,lParam
USES ebp,ebx,edi,esi
Local hMemory,stm:SYSTEMTIME,ps:PAINTSTRUCT,hdc,hdcPrn
;.WM_CREATE
cmp D[iMsg],WM_CREATE
jne >>.WM_CHAR
Maybe GoAsm is different, but with Masm a uses ebp causes serious problems when local variables are present, because ebp gets automatically saved by the frame.
OK JJ,
Took out the EBP and the same problem persists.
Thanks
Post the exe, maybe with Olly I could tell you more.
Quote from: shankle on July 15, 2012, 08:57:01 PM
This is not about my main problem but am curious why this messagebox is causing problems.
This code causes the messagebox to go into a loop from which I have to
reboot the puter to get out of it. It never goes to WM_CREATE.
WndProc:
FRAME hWnd,iMsg,wParam,lParam
USES ebp,ebx,edi,esi
Local hMemory,stm:SYSTEMTIME,ps:PAINTSTRUCT,hdc,hdcPrn
; USED ONLY DURING TESTING PHASE
DATA SECTION <--Try adding this
;TM8 db 'begin of wndproc',0
CODE SECTION <--and this
pushad <-- why?
invoke MessageBox, NULL,addr TM8,NULL,MB_OK
popad <-- why?
; test end
Are you putting the string in label TM8 right in-line with your code, or do you keep the semi-colon (comment) when you test and put the data elsewhere? If the semi-colon is removed and that's your actual data, then put "DATA SECTION" right before and "CODE SECTION" right after the string line. I would say the computer thinks the string is code and is trying to execute it. It's right in the middle of a code section. Also, jj is right--the EBP needs to come out. Is that message box the first thing in your Window procedure? If so, it's getting called
a lot.
jj,
You can cut & paste that mouseover stuff and make snarky demands all day long. That doesn't change the fact that you're unwilling to admit to pushing registers un-necessarily. I'm done with the back and forth. Insult someone else.
the MessageBox API preserved EBX, EBP, ESI, and EDI for you
you don't have anything in EAX, ECX, or EDX that needs to be preserved
WndProc:
FRAME hWnd,iMsg,wParam,lParam
USES ebx,edi,esi
Local hMemory,stm:SYSTEMTIME,ps:PAINTSTRUCT,hdc,hdcPrn
invoke MessageBox, NULL,addr TM8,NULL,MB_OK
;.WM_CREATE
cmp D[iMsg],WM_CREATE
jne >>.WM_CHAR
even if that does "work", it's going to generate a lot of message boxes - lol
when a window is created, it receives several messages
if you're not using Vista, you might be happier using Beep...
WndProc:
FRAME hWnd,iMsg,wParam,lParam
USES ebx,edi,esi
Local hMemory,stm:SYSTEMTIME,ps:PAINTSTRUCT,hdc,hdcPrn
invoke Beep,800,40
;.WM_CREATE
cmp D[iMsg],WM_CREATE
jne >>.WM_CHAR
Hi JJ,
Here is the ".exe" of my attempt at GoAsm. I have zipped it to
conform to Hutches regs.
It is NOT a working program. It will take you to the Wndproc
and die right there.
Have fun. I sure am ;)
Hi Satpro,
I sent you a message (I thought), Must have been in Sandboxie when I did it.
The TM statement is just there to remind me what I coded.
It is defined in the Data Section.
I have remove ebp.
I thought that the ";" was recognized in GoAsm as a comment. Am I wrong?
This messagebox is for testing ONLY. It will be removed as I get the program
working. Push/pop is there in the event i missed something which I obviously
have.
HI Dave,
Using Window 7 pro.
It does generate a lot of messages. Don't understand why.
Taking out the push/pop doesn't really make it any better.
Purpose in using it was to find out were my code was taking me.
Hi shankle,
Yes, the ";" is a comment. Block comments are /* and */. (no period)
I didn't know if you pulled the semi-colon to run the code. I really do think you need to move that message box call. Every time you get any kind of message that thing will run because it's first up in the WndProc.
I really hope you stick with GoAsm. I like Masm, too, but just really enjoy coding with GoAsm. Granted, there's a lot more code written for Masm and this is a Masm forum, but Jorgon really wrote a nice assembler and linker. It's not laden with lots of extras, and is basically a 32/64-bit-only assembler, which is great. It's simple, but powerful. Kind of like the assembler I started with way back when. And man, is it fast! The thing I'm doing now is about 72k with about 30 files and it finishes in a couple of seconds. I did have to read through the manual several times and usually keep it open while I program for things like syntax. I use it with Notepad++ and can keep all the files right there with asm color-coding and unreal search/replace features. It compiles & executes right from the editor. The big difference between that and qeditor, which I like, too, is the tabbed files and multiple document (side by side) views. What do you use?
Also, do you think there's any way I could look at the source (the startup, winmain, and data declaration) for your test program? Maybe print it out and look at it closer. The answer might just be "right there." You never know. I downloaded the .exe but didn't run it yet. I've got a lot of stuff opened up right now 'cuz I'm coding. But once we get your Window procedure ironed out you should be good to go. That really is the hard part in the beginning. Later, you'll laugh about it, I promise.
Hi Satpro,
Right now I'm in Frown mode.
I kind of expected it to work right out of the box.
Yes, It's going to be some kind of embarrassing error.
I started back in the 60s with the Assembler from IBM.
I like it better than Masm or GoAsm. It had a length code for everything
which I kind of miss.
It always is.
Quote from: shankle on July 16, 2012, 06:49:42 AM
Hi JJ,
Here is the ".exe" of my attempt at GoAsm. I have zipped it to
conform to Hutches regs.
It is NOT a working program. It will take you to the Wndproc
and die right there.
Have fun. I sure am ;)
Thanks. As it seems, the very first message quits with a "ret", not with calling DefWindowProc; and of course it returns to no man's land. So you better check why the WM_CREATE handler does not call DefWindowProc.
As a general remark, using a MsgBox at the entry of WndProc cannot work (I think satpro mentioned that already). You better use console assembly & link, and a pushad / print whatever / popad sequence.
HTH, jj
Thank you JJ.
You made me take another look at WM_DESTROY.
When I converted this from MASM32 to GoAsm I got the
branch wrong.
Now the 1st window is flashing on the screen and goes to
"the program has stopped working".
To tired tonight. Will give it a rest and try tomorrow to
figure it out.
The other correction would be at the end of each message that you do process to place a jmp instruction (otherwise you continue the message comparisons and eventually end up at DefWindowProc). So it should look like something this:
jmp >.Return0 ;<--- at the end of each message that you process, usually should return 0
.WM_DESTROY
cmp D[iMsg],WM_DESTROY
jne >.Default ;<--- no more messages to check, so pass do DefWindowProc
.Return0
xor eax,eax ;<--- message processed, usually return 0
.Return
ret
.Default
INVOKE DefWindowProc, [hWnd], [iMsg], [wParam], [lParam]
ret
Getting there.
GoAsm complains about the 1st branch to Out2.
As you see it, it complains that the branch is to far.
If I change it to "jl >>.Out2" it gives an error message that
it is past the end of the program.
cmp D[holdR], 5
jl >.Out2 ;** GoAsm complains about this branch**
add ebx, 4
invoke WriteToPrinter, [hdcPrn],2926,3206,1837,ebx
cmp D[holdR],6
jl >.Out2
add ebx, 4
invoke WriteToPrinter, [hdcPrn],2926,3206,2035,ebx
cmp D[holdR],7
jl >.Out2
add ebx, 4
invoke WriteToPrinter, [hdcPrn],2926,3206,2233,ebx
cmp D[holdR],8
jl >.Out2
add ebx, 4
invoke WriteToPrinter, [hdcPrn],2926,3206,2430,ebx
.Out2
I shall look into correcting the Line Number given for that GoAsm error.
I suspect that your code snippet is not within a FRAME...ENDF. In that case, your labels beginning with a period (.Out2) have a local scope extending between the unique code labels with a colon that surround it. In other words, the label for the jump in jl >>.Out2 is not found since between the jump instruction and the jump target you have at least one unique label "SomeLabel:" defined. Some solutions...
- The above does not take place within a FRAME...ENDF block; the scope of the label is local to FRAME...ENDF.
- Perhaps you can go more local with ".SomeLabel" instead of "SomeLabel:"
- Use an unscoped re-usable label made up of digits or a character+digit(s):
cmp D[holdR],5
jl >>L2
;
;
SomeLabel:
;
;
L2:
Thanks WJR for replying.
I did not fully understand what you just wrote.
So let me explain my code more fully.
The code in the snippet above is within the Wndproc frame/endf code.
They should all be of a local scope I think. At least that's what I intended.
As you can see from the snippet there is nothing between the first Out2
and the label ".Out2" other than other Out2s.
I do not get an error at my end with the required long jump, and unfortunately can not see why else an error would occur from what you have given.
If the L2 variation above does not work, then another way, since it looks like you are off by only 8 bytes, would be to avoid the long jump with shorter code... I do see some repetitive memory access that could be avoided with register usage if esi or edi were available and loaded with and then used instead of [holdR].
i don't know if GoAsm uses processor directives or not
but, if he has it set for .386, it might cause troubles
Not using ".386".
The code relating to "Out2" is working now. Have no idea why
it didn't work in the 1st place. Moving on....
The following code is causing the program to fail. I get a clean
compile and link. It is caused by the "SetMapMode" API.
No matter if I do this "wm_text or [wm_text]" the instruction
causes the program to fail. This instruction works fine in MASM32:
;in data section
colortxt dd 00ffffffh ; white
; in code section
WndProc:
FRAME hWnd,iMsg,wParam,lParam
USES ebx,edi,esi
Local hMemory,stm:SYSTEMTIME,ps:PAINTSTRUCT,hdc,hdcPrn
.WM_PAINT
cmp D[iMsg],WM_PAINT
jne >>.GetOut2
invoke BeginPaint, [hWnd],[ps]
mov [hdc],eax
invoke SetTextColor, [hdc],[colortxt] ; text color white
invoke SetBkMode, [hdc],TRANSPARENT
invoke SetMapMode,[hdc],MM_TEXT
; (lots of code here)
.GetOut2
cmp D[iMsg],WM_DESTROY
jne >.default
invoke PostQuitMessage,NULL
.return0
xor eax,eax
ret
.default
invoke DefWindowProc, [hWnd],[iMsg],[wParam],[lParam]
ret
ENDF
Although I saw it to Begin with, another potential cause of failure would be the hdc value, and tracing that back to the source gives this correction:
invoke BeginPaint, [hWnd], ADDR ps
Can't get this code to work in GoAsm.
; from the SystemTime structure
xor ebx,ebx
mov bx, [stm.wDay]
; stm.day has the value of 14h
;Doesn't work
cmp D[ebx],0000000eh
jbe >>.Somewhere
jmp >.ddd
; short jump
.ddd
;eeej defined in the data section
eeej dd 0000000eh
;Doesn't work
cmp D[ebx],addr eeej
jbe >>.Somewhere
jmp >.ddd
; short jump
.ddd
; from the SystemTime structure
xor ebx,ebx
mov bx, [stm.wDay] ; or: movzx ebx, W[stm.wDay] and eliminate the xor code
; stm.day has the value of 14h
;Doesn't work
cmp D[ebx],0000000eh ;this is comparing the value at the address contained in ebx to 0eh ( try: cmp ebx, 0eh [means cmp 14h to 0eh])
jbe >>.Somewhere ;there is no address in ebx, only a value (14h)
jmp >.ddd
; short jump
.ddd
You have a System Time Value in bx. The brackets around ebx are telling it to treat the value in ebx (the day) as a ptr to an address (00000014h), which doesn't exist in your process space (means compare value at address 00000014h to 0eh). Does it crash?... If all you want to do is compare the value held in ebx to the number 0eh, then just remove the brackets (and the D--it's implied in this case). The 'D" is like "DWORD PTR" in Masm, just shortened up a bit in GoAsm. try: cmp ebx, 0eh (means cmp 14h to 0eh)
The instruction "movzx" will put a dword into ebx for you, padding the word from [stm.wDay] with zeroes. Then you will no longer need to clear ebx first
;eeej defined in the data section
eeej dd 0000000eh
;Doesn't work
cmp D[ebx],addr eeej
jbe >>.Somewhere
jmp >.ddd
; short jump
.ddd
[/size][size=78%]Would cmp ebx, [eeej] work for you? (to compare what's in ebx to what's in eeej). Again, in this case ebx IS a dword, so it only compares to dwords--no "D" needed here.[/size]
Hi Satpro,
Will give your suggestions a try.
The "moczx" wants a "D" in the movzx instruction..
Then you have to put [] around eax.
Obviously I'm having syntax problems.
To [] or not to [].
To addr or [].
Changes from "if" to "cmp" is causing major problems.
Coming along, I now get my 1st screen and then
the message "My Prog has stopped working".
What I need to do is go from the 1st screen to "WM_CHAR"
to input data.
Yes I read the manual but not with 100% understanding.
Time will cure all I suppose.....
If the value in [stm.wDay] is a word then you code ->movzx ebx, W[stm.wDay]. It means you're moving a zero-extended WORD value into ebx. It adds 16 zeroes to a word value. If it was a byte you'd use a "B" and it would add 24 zeroes. In GoAsm the use of brackets when addressing memory is mandatory. Brackets around a register works the same. No brackets around a register refers to what's in that register--not some address. Let's say you do this:
DATA SECTION
GoAsm dd 255
CODE SECTION
mov esi, ADDR GoAsm ;get the address of the GoAsm variable into esi
Now, to access variable [GoAsm] using esi:
mov eax, [esi] ;this time no "D" because eax is 32-bits--that's known. It expects a DWORD
;eax will now contain "0x000000FF"
But, if you do this:
movzx eax, W[esi] ;you need the "W" because you're accessing something less than a full DWORD
inc B[esi] ;you need the "B" (or W or D) because you're incrementing what's in the address esi is pointing to
That inc affects flags, so it matters what size you're working with. Pretending the value you are incrementing = 255 ($FF) a BYTE would roll over to 0, setting the Zero flag, but a WORD or DWORD won't. A WORD would have to = 65535 ($FFFF) to inc and roll over to 0. So you need to specify what size you're working with. GoAsm doesn't type-check, so that 255 in variable [GoAsm] can be interpreted in different ways; that is, 1,2 or 4 bytes. Memory works the same way:
inc W[GoAsm] will work on a WORD, the first 2 bytes only, even though [GoAsm] is declared as 32-bits.
Let's say:
GoAsm dw 255 (WORD)
Provided it's not at the end of your declared data, you can still access a DWORD at variable [GoAsm]:
cmp D[GoAsm], 3
ADDR works with the address of some label. It won't tell you what's in memory there. That's what the brackets do.
Quote from: satpro on July 19, 2012, 11:50:33 AMGoAsm doesn't type-check, ...
GoAsm dw 255 (WORD)
So would a simple
inc [GoAsm] throw an error, or would it be encoded using a default size, i.e. DWORD? Masm and JWasm would use WORD in this case.
Hi jj,
It would give you a type expected (B,W, or D) error.
Thanks Satpro.
It worked just fine.
Got a lot of the bugs out of My Prog. It is now giving this message in WM_Create
using GetLastError code: "data area passed to a system call is to small".
The EnumPrinter has the cbBuf set to zero.
I tried to use "dwtoa" to find out what was in the pcbNeeded parameter
but it did not execute. I obviously have some [] or addr incorrect.
The DWTOA code is by WJR.
.data
dwNeeded dd 0
dwReturned dd 0
TM18 db 'dwNeeded',0
convfld db ' ',0
.code
; other goodies
WndProc:
FRAME hWnd,iMsg,wParam,lParam
USES ebx,edi,esi
Local hdc,hdcPrn
;.WM_CREATE
cmp D[iMsg],WM_CREATE
jne >>.WM_CHAR
invoke EnumPrintersA, PRINTER_ENUM_LOCAL,NULL,4,NULL,0,\
[dwNeeded],[dwReturned]
; test begin
; edx is the input field
; ebx convfld is the output field
pushad
mov edx, [dwNeeded]
mov ebx, addr convfld
mov [BufAdd],ebx
invoke dwtoa, edx, [BufAdd] ; Hex DD to string
invoke MessageBox, NULL,[TM18],[convfld], MB_OK
popad
; test end
jmp >>.default
; other goodies
;code per WJR
dwtoa:
FRAME dwValue,lpBuffer
USES esi,edi
; -------------------------------------------------------------
; convert DWORD to ascii string
; dwValue is value to be converted
; lpBuffer is the address of the receiving buffer
; EXAMPLE:
; invoke dwtoa,edx,addr buffer
;
; Uses: eax, ecx, edx.
; -------------------------------------------------------------
mov eax,[dwValue]
mov edi,[lpBuffer]
test eax, eax ; is the value negative
jns >
mov B[edi], '-' ; store a minus sign
inc edi
neg eax ; and invert the value
:
mov esi, edi ; save pointer to first digit
mov ecx, 10
.convert
test eax,eax ; while there is more to convert
jz >
xor edx, edx
div ecx ; put next digit in edx
add dl, '0' ; convert to ASCII
mov [edi],dl ; store it
inc edi
jmp <.convert
:
mov B[edi], 0 ; terminate the string
.reverse ; We now have all the digits,
; but in reverse order.
cmp esi,edi
jae >
dec edi
mov al,[esi]
mov ah,[edi]
mov [edi],al
mov [esi],ah
inc esi
jmp <.reverse
:
ret
endf
the idea is
1) call the function once to get the required buffer size by setting cbBuf to 0
2) allocate the buffer - size = cbNeeded
3) call the function again to fill the buffer - this time, pass a pointer to the buffer and it's size
it can be used with a pre-determined buffer the first time, of course
but - the code is probably simpler if you just get the size first
You are correct in where you are obviously incorrect. For a 32-bit program, (most) arguments passed to a function are 32-bit, so generally if they are looking for a string or a more complicated structure, what you pass to the function is a pointer to that string or structure. In GoAsm syntax, that would require something like ADDR SymbolName, so one correction is here:
invoke MessageBox, NULL, ADDR TM18, ADDR convfld, MB_OK
If you use [SymbolName], that passes a 32-bit value. If you look at the API documentation for a function, those parameters that have an LP prefix are going to require a pointer with ADDR. In the case of EnumPrinters, you have correctly defined DWORDs to hold the values, but the function requires a pointer to these, so the next correction:
invoke EnumPrintersA, PRINTER_ENUM_LOCAL,NULL,4,NULL,0,\
ADDR dwNeeded, ADDR dwReturned
to add to what Wayne stated...
the "lp" prefix is usually used to refer to a "long pointer" - which is a 32-bit address
in the case of this function, they just used "p" for "pointer"
in a 32-bit program, "p" also refers to a 32-bit address
that can be a little confusing :biggrin:
as an example, one of the EnumPrinters function parameters is named "pcbNeeded"
that means they want you to pass the address of a dword variable that might be named "cbNeeded"
while "cbNeeded" refers to a byte-count, it is a dword in size :P
damned Hungarians ;)
Thanks all for the help.
The previous items in WM_CREATE seem to check out fine now.
However the program is still not printing.
I have included a snippet of the code that might be in error.
Of course it could be something else.
.data
PT2 db '01020207010302040105020602030208',0
db '01010103020501070208010402060106',0
db '01040102010102060207010802010105',0
saveebx dd 0
holdR db " ",0
DOCINFOA <sizeof(DOCINFOA),AppName,NULL,NULL,0>
; other code
WndProc:
FRAME hWnd,iMsg,wParam,lParam
USES ebx,edi,esi
Local hdcPrn
; other code
invoke StartDoc, [hdcPrn],addr doci
lea ebx,PT2
mov [saveebx],ebx ; address PT2 in saveebx
; other code
mov ebx, [saveebx] ; address in ebx
; not sure if ebx should have " []" or not in invoke
invoke WriteToPrinter, [hdcPrn],393,660,1045,ebx
; other code
WriteToPrinter FRAME hdcPrn,Horz1,Horz2,Vert4,LpBuffer
; Have no idea which of the following two instructions are correct
mov ecx,addr LpBuffer ; address in LpBuffer of PT2
mov ecx,[LpBuffer] ; address in LpBuffer of PT2
;This line is causing the problem - address in ecx should move data to ax
;Have a messagebox here and the program boombs after it.
mov ax,W[ecx]
cmp al,030h
jne >
mov al,020h
:
mov W[holdR],ax
invoke TextOutA, [hdcPrn],[Horz1],[Vert4],[holdR],2
ret
ENDF
Check
holdR db " ",0
The upper byte of that "fake dword" used in TextOut gets supplied by DOCINFOA. Now if the lowest byte of DOCINFOA contains something different from zero, you are in trouble ;-)
Edit: Sorry, I just saw you are using only 2 bytes. Disregard this post, please.
I believe what you had originally was correct and the problem was elsewhere. If your program doesn't work and you are coming from a working MASM program, then the error(s) are somewhere in your translation to GoAsm syntax, and you should really be stepping through your code in a debugger to quickly track these down. Another alternative is re-comparing to the MASM source code. The "not sure" and "have no idea" are a little worrisome. There is a big difference between [ebx] and ebx, and the syntax in this case is the same between the two assemblers (although in other cases some differences with GoAsm B/W/D/Q prefix vs MASM PTR variations). There is also a big difference between [LpBuffer] and ADDR LpBuffer (for the first one, GoAsm requires the [] where MASM allows LpBuffer... for the second one, if I recall correctly you would see MASM with OFFSET LpBuffer).
in masm, OFFSET is used for globals
ADDR can be used for either locals or globals - i prefer to use it only for locals
In GoAsm both ADDR and OFFSET are accepted.
From the GoAsm manual:"In order to get the offset in GoAsm you must use
MOV EBX,ADDR wParam
or if you prefer
MOV EBX,OFFSET wParam
which means the same thing"
For MASM ADDR is valid only with INVOKE.
Been over this before but I still am not able to print anything.
So I decide to start testing in WM_CREATE again.
MASM32 was using "winspool.inc" for the printer.
EnumPrintersA fails with a buffer to small error
.data
dwNeeded dd 0
dwReturned dd 0
pinf04 dd 0
.code
; other code
WndProc:
FRAME hWnd,iMsg,wParam,lParam
USES ebx,edi,esi
Local hMemory,hdc,hdcPrn
;.WM_CREATE
cmp D[iMsg],WM_CREATE
jne >>.WM_CHAR
invoke EnumPrintersA, PRINTER_ENUM_LOCAL,NULL,4,NULL,0,\
addr dwNeeded,addr dwReturned
invoke GlobalAlloc, GHND,addr dwNeeded
mov [hMemory], eax
invoke GlobalLock, eax
mov [pinf04],eax
mov ebx,eax
invoke EnumPrintersA, PRINTER_ENUM_LOCAL,NULL,4,[pinf04],\
addr dwNeeded,addr dwNeeded,addr dwReturned
lea eax, [ebx+PRINTER_INFO_4.pPrinterName]
invoke CreateDC, NULL,eax,NULL,NULL
mov [hdcPrn],eax
mov [hdc],eax
invoke GlobalUnlock, [hMemory]
invoke GlobalFree, [hMemory]
jmp >>.default
; other code
endf
invoke GlobalAlloc, GHND,addr dwNeeded
The second argument should be [dwNeeded].
In the second call to EnumPrinters, the third last argument cbBuf should also be [dwNeeded].
I'll try one more time and then I'll have to abandon Goasm.
I know I have been a pest - sorry.
MASM32 was using "winspool.inc" for the printer.
Enumprinter 2 never gets the name of the printer in parmeter 4 (PrintCString).
It shows executing clean.
Therefor CreatDC does not work.
I have been playing with this for days.
PrintDlgEX won't work
I can get the Buffer length in dwNeeded which is 72.
.data
dwNeeded dd 0
dwReturned dd 0
PrintCString db 90 DUP 0 ; buffer to receive the printer name
.code
; other code
WndProc:
FRAME hWnd,iMsg,wParam,lParam
USES ebx,edi,esi
Local hMemory,hdc,hdcPrn
;.WM_CREATE
cmp D[iMsg],WM_CREATE
jne >>.WM_CHAR
invoke EnumPrinters, PRINTER_ENUM_LOCAL,NULL,4,NULL,\
0,addr dwNeeded,addr dwReturned
invoke GlobalAlloc, GHND,[dwNeeded]
mov [hMemory], eax
invoke GlobalLock, eax
; mov ebx,eax
invoke EnumPrinters, PRINTER_ENUM_LOCAL,NULL,4,addr PrintCString,\
[dwNeeded],addr dwNeeded,addr dwReturned
; lea edx, [ebx+PRINTER_INFO_4.pPrinterName]
invoke CreateDC, NULL,addr PrintCString,NULL,NULL
mov [hdcPrn],eax
invoke GlobalUnlock, [hMemory]
invoke GlobalFree, [hMemory]
jmp >>.default
endf
Although there is a bit of a learning curve with GoAsm or any other assembler, there have been occasions here where the problem seems related more to the lower level understanding of pointers and their use in API functions.
Another correction in your earlier code, use mov instead of lea:
mov eax, [ebx+PRINTER_INFO_4.pPrinterName]
Your EnumPrinters returns an array of PRINTER_INFO_4 structures. CreateDC requires a pointer to a string for the name of the device (the first member of PRINTER_INFO_4), but with your recent "addr PrintCString" you are incorrectly passing the pointer to a PRINTER_INFO_4 structure. Note that there can be more than one of these structures, so perhaps you have more code that advances ebx to select which printer. There is also the GetDefaultPrinter API function.
I am in the process of getting printing operational for a program. I wasn't quite there yet, but unfortunately even with these corrections, the call to CreateDC fails. I have seen some sample code with the first argument as "WINSPOOL", but the documentation does recommend NULL for a printer. That sample code does pass a pointer to a DEVMODE structure though, perhaps that is the missing piece of the puzzle (although NULL does seem like it should work for a default initialization).
TinyIDE has a printing routine. Look for PrintRTF inside the (Masm, not GoAsm) source, maybe you can get some inspiration. The *.asc file is the source of the source and opens in TinyIDE.exe.
"I'll try one more time and then I'll have to abandon Goasm"
Well, is that fair?
In the first call to EnumPrinters the 4th param is NULL. Doesn't that need to be a ptr to a buffer that receives an array of PRINTER_INFO_4 structures? WinHlp doesn't mention anything about specifying a NULL ptr. Then the 5th param should be a buffer size number, right? Then, if more detailed info is needed, a 2nd call with a level 2 info structure can be called later.
Where are the comments (e.g. 1 per line of code?) that help you with what's going on? Also, error-checking all OS calls would be a GREAT idea. EnumPrinters returns TRUE if the function succeeds. Same with GlobalAlloc (a ptr). Where are the checks for success or failure? You would know the call is not succeeding (and why) right then and there. And then, why not use GMEM_FIXED | GMEM_ZEROINIT in GlobalAlloc and skip the Lock/Unlock calls, along with their lock counts? The returned handle in this case is a ptr to your memory and you're freeing it almost immediately anyway, not that it matters. It's your memory to use.
Wayne's advice about lea is right on the money. You can refer to the values in a structure using the [register + structure.member] approach. The assembler will handle the lea for you.
Maybe, and this is just a thought, how about try this in a console first, where you don't have the worries of the window proc heaped on top of a complicated series of calls to EnumPrinters? It's really not that much code. Move it back to the window when you iron everything out.
It doesn't seem right that the assembler should have to take the fall here...
Hi Satpro,
I've done all the error checking it's just not posted here. I also have comments and
their not posted here. Trying to keep it simple and straight forward.
It fails at CreateDC, Handle is not created. My understanding of the 1st EnumPrinter
is that it is supposed to fail and only return the length of the buffer.
Okay. The call should be set up to succeed. Make sure the buffer is large enough and that you use valid pointers. The INFO_4 structure is 12 bytes per device. Two ptrs and an attributes dword. You said you needed 72 bytes, right? 90 should be fine, except it's not a multiple of 12. Of course, the call still reports what you would need. Or, use the larger buffer when you don't know the answer. It's only a few bytes in DATA, right?
Also, "WINSPOOL",0 is used with computers after Win95/98. And I just thought of something else.
Try this:
DATA SECTION
winspoolstring db "WINSPOOL", 0
CODE SECTION
mov ebx, ADDR PrintCString (your buffer)
invoke CreateDC, addr winspoolstring, [ebx+PRINTER_INFO_4.pPrinterName], NULL, NULL
You can do this instead and GoAsm places the 0-term string 'WINSPOOL' in the CONST section for you (or DATA if no CONST or CODE if no DATA section):
CONST SECTION
CODE SECTION (this might be easier for you)
mov ebx, ADDR PrintCString (your buffer)
invoke CreateDC, 'WINSPOOL', [ebx+PRINTER_INFO_4.pPrinterName], NULL, NULL
Here's why:
The INFO_4 structure contains ptrs as it's first two members, and you want the first ptr. Therefore, you would want the data at that address, which is the address of the string you need, contained within the structure, and not the address of the structure itself. This means brackets and not ADDR. Data, not address.
I have to admit that in using GoAsm you are working without a net most of the time because hardly anyone here uses it. From my own experience I have found it to be a very efficient assembler that does ANYTHING I need it to do. Not that Masm doesn't. Once you get the hang of its (actually very simple) syntax there is nothing you cannot accomplish with it, and with no extra overhead. But I don't use 'C' calls at all and macros almost never. So for me GoAsm is ideal. I'm not kidding--I had to read the manual a lot more than once and still refer to my printed copy frequently, although less and less. But my source seems much easier to read with GoAsm; I like that very much.
In Masm you don't use ADDR much, maybe just with invoke. The Masm guys already stated that. If you feel more comfortable using OFFSET--use that instead. Just remember that when accessing a variable's contents (its data) you use [somevariable] and in Masm you don't have to but can if you want --> somevariable or [somevariable].
Keep plugging away...
Bert
Quote from: satpro on July 31, 2012, 10:17:50 AM
Okay. The call should be set up to succeed. Make sure the buffer is large enough and that you use valid pointers. The INFO_4 structure is 12 bytes per device. Two ptrs and an attributes dword.
The size would be 12 bytes in 64-bit code?
Good catch, and no it wouldn't, but we were talking 32-bit code.
Hi Satpro,
Thank you for replying.
Don't misunderstand me I am not blaming GoAsm for my inadequacies.
I have incorporated your suggestions and still no luck as you can see below.
I don't use macros either as I hate to dig all over the place to find out what
they need or mean. Same with includes. Only when necessary.
The next 3 lines are a quote from the Microsoft CreateDC API.
lpszDriver
"A pointer to a null-terminated character string that specifies either DISPLAY or the name
of a specific display device. For printing, we recommend that you pass NULL to lpszDriver
because GDI ignores lpszDriver for printer devices".
Enumprinter 2 puts garbage in parmeter 4 which is PrinterCString.
Therefor CreatDC does not work.
.data
dwNeeded dd 0
dwReturned dd 0
PrintCString db 96 DUP 0 ; buffer to receive the printer name
winspoolstring db "WINSPOOL",0
.code
; other code
WndProc:
FRAME hWnd,iMsg,wParam,lParam
USES ebx,edi,esi
Local hMemory,hdc,hdcPrn
;.WM_CREATE
cmp D[iMsg],WM_CREATE
jne >>.WM_CHAR
invoke EnumPrinters, PRINTER_ENUM_LOCAL,NULL,4,NULL,\
0,addr dwNeeded,addr dwReturned
invoke GlobalAlloc, GHND,[dwNeeded]
mov [hMemory], eax
invoke GlobalLock, eax
invoke EnumPrinters, PRINTER_ENUM_LOCAL,NULL,4,addr PrintCString,\
[dwNeeded],addr dwNeeded,addr dwReturned
; test begin
;tm2 db 'got to WM_CREATE',0
pushad
invoke MessageBox, NULL,addr TM2,addr PrintCString,MB_OK
popad
; test end
;The result of this test shows "yo@" with a tilder over the "o" in PrintCString.
;So good data is not getting into PrintCSring even though EnumPrinter is not
; failing. Of course this causes CreatDC to also fail.
mov ebx,addr PrintCString
invoke CreateDC, addr winspoolstring,[ebx+PRINTER_INFO_4.pPrinterName],NULL,NULL
mov [hdcPrn],eax
invoke GlobalUnlock, [hMemory]
invoke GlobalFree, [hMemory]
endf
Shankle,
I've read that about WINSPOOL, too. The book I use for reference is the Windows 2000 API SuperBible and it makes a distinction between Win95/98 and 2000, which is more like today's OS. It talks about WINSPOOL. Maybe I'm mis-reading something there.
But I'll look into it today.
P.S. Did you try the suggestion about using the ptrs on the first call to EnumPrinters? Oh--if you're getting info on 6 devices (72 bytes) the first array in PrintCString might not be what you're looking for--just a guess. The rest would offset by 12. You are compiling Win32 and for Ansi chars, right?
Shankle,
After writing the last post to you I got to thinking. You know, I feel like I'm just throwing guesses out there without knowing what I should know to actually help. At the very least it's unfair to you. At it's worst I look stupid for throwing out guesses. There is just this small bit of visible code to go on that tells very little about the actual program.
How about this? Zip up your GoAsm folder and email it to me. I promise no judgments on coding style or any of that. I have this console "debugger" I wrote that tells me variables, flags, etc. I use it a lot for tricky new stuff. On top of that I bought the paid version of GoBug so I really will find the cause of this trouble. But doing it this way now is getting all of us nowhere fast.
My idea is to isolate the Enum stuff and throw it into the console, and actually work with your code. It probably won't take forever to find the answer.
You don't have to do that, but I think any more guessing is just "poking and hoping."
Bert
Hi Satpro,
I have only 1 printer(HP Officejet Pro l7580).
Yes it is ANSI.
I don't mind the suggestions and I do try to test them.
I understand the problem of trying to help someone with your hands tied.
However IMHO the other parts of the program have nothing to do with
the EnumPrinters, CreateDC, GetDefaultPrinter, PrintDlgEX. All of which don't
work for me.
As mentioned, with the corrections, your second call to EnumPrinters works just fine. Your PrintCString buffer gets filled with PRINTER_INFO_4 structures. To see this, correct your test as follows:
invoke MessageBox, NULL,addr TM2,[PrintCString],MB_OK
invoke MessageBox, NULL,addr TM2,[PrintCString+12],MB_OK
invoke MessageBox, NULL,addr TM2,[PrintCString+24],MB_OK
invoke MessageBox, NULL,addr TM2,[PrintCString+36],MB_OK
invoke MessageBox, NULL,addr TM2,[PrintCString+48],MB_OK
invoke MessageBox, NULL,addr TM2,[PrintCString+60],MB_OK
It seems like your call to CreateDC may require a pointer to a DEVMODE structure as the last argument (perhaps the 64-bit drivers are more picky if this was previously working on 32-bit).
I went a slightly different route with:
GetDefaultPrinter
OpenPrinter
DocumentProperties ;to get size of buffer for DEVMODE
GetProcessHeap
HeapAlloc
DocumentProperties
CreateDC
I did get this to work up to this point. However, for some reason ONLY while debugging, the first call to DocumentProperties fails. Searching the web, there does appear to be some difficulties with this function, as well as PrintDlgEx, on more recent 64-bit OS's.
Thank you WJR,
I'm still working on the 32-bit version.
Haven't started to worry about the 64-bit code.
The 2nd EnumPrinter is now working correctly.
I can see my printer in the test messagebox.
So now the problem has moved to the "CREATEDC" api.
More fun.
The 1st window is written to the screen for me to enter data for the program.
Then this wonderful Windows message appears: "program has stopped working".
I never get a chance to enter the data that's needed. Yes, I have probably screwed
up GoAsm or Windows or both somehow. Therefor wParam never gets loaded
with the keyed input before the Error message appears.
I suggest you run your program in a debugger. E.g. the freeware OllyDbg. This way you will see the offending command.
I have shown below what happens in GoBUG at the end of WM_PAINT after the
1st screen is created. It shows the screen and then a Windows message is
presented " the program has ceased to function". ".doneh" is at the very end
of WM_PAINT. If I insert a message box right after ".doneh", it lets me enter
my data and fails again for other reasons. This is my 1st attempt with Go Bug.
;------------------------------------------------------------------------
GoBug - copyright Jeremy Gordon 1996-2009
Date: Sunday, August 12, 2012 Time: 11:51:18 AM
Dump of codepane
~~~WndProc.doneh:~~~>
4045E4: PUSH EBP
4045E5: ADD D[ESP],-54
4045E9: PUSH [EBP+14]
4045EC: CALL EndPaint(USER32.dll)
4045F1: MOV ESP,EBP
4045F3: POP ESI
4045F4: POP EDI
4045F5: POP EBX
4045F6: POP EBP
4045F7: RET 10
~~~WndProc.WM_DESTROY:~~~>
4045FA: CMP D[EBP+18],2
4045FE: JNZ >404608
404600: PUSH [EBP+20]
404603: CALL PostQuitMessage(USER32.dll)
~~~WndProc.default:~~~>
404608: PUSH [EBP+20]
40460B: PUSH [EBP+1C]
40460E: PUSH [EBP+18]
404611: PUSH [EBP+14]
404614: CALL DefWindowProcA(USER32.dll)
404619: MOV ESP,EBP
40461B: POP ESI
40461C: POP EDI
40461D: POP EBX
40461E: POP EBP
40461F: RET 10
4045E4: PUSH EBP
4045E5: ADD D[ESP],-54
4045E9: PUSH [EBP+14]
4045EC: CALL EndPaint(USER32.dll)
4045F1: MOV ESP,EBP
4045F3: POP ESI
4045F4: POP EDI
4045F5: POP EBX
4045F6: POP EBP
4045F7: RET 10
there must be more to it than that :P
the stack frame looks a little strange, there
we don't see the call to BeginPaint, nor the initialization of the stack frame
or - maybe that's it - lol
the sub 54h sounds like it might be making space for a PAINTSTRUCT
Jack...
i have repeatedly mentioned that you might post the full source
we have a hard time doing much in bits and pieces
it's like putting together a jigsaw puzzle with only 10% of the pieces