News:

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

Main Menu

A window in 30 lines of code, using the Masm32 SDK

Started by jj2007, November 20, 2023, 03:24:09 AM

Previous topic - Next topic

jj2007

A simple template demonstrating how elegant and efficient Assembly can be

include \masm32\include\masm32rt.inc ; **** load standard Masm32 includes ****
.data ; initialised data section
wc WNDCLASSEX <WNDCLASSEX, 0, WndProc, 0, 0, 1, 2, 3, COLOR_BTNFACE+1, 0, txClass, 4>
txClass db "Masm32GUI", 0 ; class name, will be registered below
msg MSG <>
.code
WinMain:
  mov wc.hInstance, rv(GetModuleHandle, 0) ; rv ("return value") is a Masm32 macro
  mov wc.hIcon, rv(LoadIcon, eax, IDI_APPLICATION)
  mov wc.hIconSm, eax ; the rv macro returns results in eax
  invoke RegisterClassEx, addr wc ; the window class needs to be registered
  invoke CreateWindowEx, WS_EX_ACCEPTFILES, wc.lpszClassName, chr$("Hello World"), ; title
     WS_OVERLAPPEDWINDOW or WS_VISIBLE, 120, 120, 300, 120, ; style, x, y, width, height
     NULL, rv(LoadMenu, wc.hInstance, 100), wc.hInstance, NULL
  .While 1
inc rv(GetMessage, addr msg, 0, 0, 0)
shr eax, 1
.Break .if Zero? ; 0 (OK) or -1 (error)
invoke TranslateMessage, addr msg
invoke DispatchMessage, addr msg
  .Endw
  invoke ExitProcess, msg.wParam
WndProc proc hWnd, uMsg, wParam:WPARAM, lParam:LPARAM
  .if uMsg==WM_DESTROY
invoke PostQuitMessage, NULL
  .endif
  invoke DefWindowProc, hWnd, uMsg, wParam, lParam ; default processing
  ret
WndProc endp
end WinMain

NoCforMe

Impressive. But didn't you leave out the lines in the resource file? That menu didn't just appear out of thin air ...
Assembly language programming should be fun. That's why I do it.

jj2007


TimoVJL

From WinMin examples:
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <shellapi.h>

#pragma comment(lib, "shell32.lib")

static LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
static void OnDropFiles(HWND hWnd, HDROP hDrop);

static TCHAR *szAppName = TEXT("WinFrame");
static TCHAR *szFrameClass = TEXT("cWinFrame");
static WNDCLASSEX wcx = {sizeof(WNDCLASSEX), CS_HREDRAW | CS_VREDRAW, 0, 0, 0,
0, 0, 0, (HBRUSH)COLOR_3DSHADOW, 0, 0, 0};
HWND g_hFrame;
HANDLE g_hInst;

void __cdecl WinMainCRTStartup(void)
{
ExitProcess(WinMain(GetModuleHandle(NULL),0,0,SW_SHOW));
}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)
{
MSG msg;
wcx.hInstance = hInstance;
wcx.lpfnWndProc = WndProc;
wcx.lpszClassName = szFrameClass;
wcx.hCursor = LoadCursor(NULL, IDC_ARROW);

if (!RegisterClassEx(&wcx))
return 0;
g_hInst = hInstance;

g_hFrame = CreateWindowEx(0, szFrameClass, szAppName,
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT,
NULL, NULL, hInstance, NULL);
if(!g_hFrame) return 0;
ShowWindow(g_hFrame, nCmdShow);
DragAcceptFiles(g_hFrame, TRUE);
while(GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
DragAcceptFiles(g_hFrame, FALSE);
return msg.wParam;
}

static LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch(uMsg) {
// case WM_PAINT:
// case WM_CREATE:
case WM_DROPFILES: OnDropFiles(hWnd, (HDROP)wParam); return 0;
case WM_DESTROY:
PostQuitMessage(0);
return(0);
}
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}

static void OnDropFiles(HWND hWnd, HDROP hDrop)
{
TCHAR szTmp[260];
int nCnt = DragQueryFile(hDrop, -1, 0, 0);
if (nCnt) {
int nLen = DragQueryFile(hDrop, 0, szTmp, sizeof(szTmp));
MessageBox(hWnd, szTmp, "File:", MB_OK);
}
}
May the source be with you

jj2007

Quote from: TimoVJL on November 20, 2023, 09:27:57 PMFrom WinMin examples:

72 lines of C code, nice :thumbsup:

With the right tools and libraries, one can do fantastic things:
GuiParas equ "Hello World", w200, h400, b LiteBlueGreen, icon Printer    ; width+height, background colour
GuiMenu equ @File, &Open, &Save, &Print, -, E&xit, @Edit, Undo, Copy, Paste
include \masm32\MasmBasic\Res\MbGui.asm
Event DropFiles    ; loads the Files$() array; use wRec$() to convert Utf8 file names to true Unicode
 wMsgBox 0, wRec$(Join$(Files$())), "Files dropped:", MB_OK or MB_TOPMOST
 StoreUtf8 "~tmpDropped.txt", Files$()    ; write dropped filenames to a text file in Utf8 format
GuiEnd

Seven lines of code, purest Assembly (plus, it writes the filenames to disk, see attachment...) ;-)

TimoVJL

Quote from: jj2007 on November 21, 2023, 11:42:36 PM...With the right tools and libraries, one can do fantastic things:
with that principle, just one line is enough ?
Those who have read Dr. Dobbs Magazine, knowns, what i am talking about  :smiley:
May the source be with you

NoCforMe

Yeah, I think this whole "look what I can do in n lines of code!" business is far too overrated. After all, sometime in the future, after neural-machine links are perfected** and all, our code will reduced to this:
DWIM
which of course is the instruction for "do what I mean".

I especially think using macros in this way is cheating, as they compound their constituent "lines of code" (instructions) into one deceptive-looking "instruction":
  mov wc.hInstance, rv(GetModuleHandle, 0) ; rv ("return value") is a Masm32 macro
should properly be expanded into what it really is, which is
  invoke GetModuleHandle, 0
  mov wc.hInstance, eax
(and if you're a purist, which I am not, you could even argue that "invoke" should be expanded into its push-push-call constituents)

** I sincerely hope I'm not still alive when that happens.
Assembly language programming should be fun. That's why I do it.

jj2007

Quote from: NoCforMe on November 22, 2023, 08:53:08 AM(and if you're a purist, which I am not, you could even argue that "invoke" should be expanded into its push-push-call constituents)

That's exactly the point: where to draw the line? I find it silly to use push-push-call for CreateFont - it's so easy to count 13 instead of 14 args, and thus run into real problems because a stack imbalance often gets not noticed because of the stack frame.

So I use invoke CreateFont, ... 14 args ..., and the assembler will tell me if I counted wrong. Only that invoke is severely broken for the Masm64 SDK, it will not tell you that you missed an arg, or added one too many.

Of course, instead of using Recall, I could paste a dozen lines of code to simulate get text from a file and put it into an array. Will it make my code more readable? Will it make me happier because I demonstrate that I can code pure assembly copy & paste?

I have over 4,000 sources in my Masm32 folder. I refuse to use "pure" Assembly.

jj2007

Update: attached a rather bloated 37 lines window template, including a Gdi32 demo - enjoy :thup:

daydreamer

Wonder if guga can beat that with oneliner, with his multiple mnemonics on one line caps assembler ?
my none asm creations
https://masm32.com/board/index.php?topic=6937.msg74303#msg74303
I am an Invoker
"An Invoker is a mage who specializes in the manipulation of raw and elemental energies."
Like SIMD coding

NoCforMe

An example:
long h[4];t(){h[3]-=h[3]/3000;setitimer(0,h,0);}c,d,l,v[]={(int)t,0,2},w,s,I,K
=0,i=276,j,k,q[276],Q[276],*n=q,*m,x=17,f[]={7,-13,-12,1,8,-11,-12,-1,9,-1,1,
12,3,-13,-12,-1,12,-1,11,1,15,-1,13,1,18,-1,1,2,0,-12,-1,11,1,-12,1,13,10,-12,
1,12,11,-12,-1,1,2,-12,-1,12,13,-12,12,13,14,-11,-1,1,4,-13,-12,12,16,-11,-12,
12,17,-13,1,-1,5,-12,12,11,6,-12,12,24};u(){for(i=11;++i<264;)if((k=q[i])-Q[i]
){Q[i]=k;if(i-++I||i%12<1)printf("\033[%d;%dH",(I=i)/12,i%12*2+28);printf(
"\033[%dm  "+(K-k?0:5),k);K=k;}Q[263]=c=getchar();}G(b){for(i=4;i--;)if(q[i?b+
n[i]:b])return 0;return 1;}g(b){for(i=4;i--;q[i?x+n[i]:x]=b);}main(C,V,a)char*
*V,*a;{h[3]=1000000/(l=C>1?atoi(V[1]):2);for(a=C>2?V[2]:"jkl pq";i;i--)*n++=i<
25||i%12<2?7:0;srand(getpid());system("stty cbreak -echo stop u");sigvec(14,v,
0);t();puts("\033[H\033[J");for(n=f+rand()%7*4;;g(7),u(),g(0)){if(c<0){if(G(x+
12))x+=12;else{g(7);++w;for(j=0;j<252;j=12*(j/12+1))for(;q[++j];)if(j%12==10){
for(;j%12;q[j--]=0);u();for(;--j;q[j+12]=q[j]);u();}n=f+rand()%7*4;G(x=17)||(c
=a[5]);}}if(c==*a)G(--x)||++x;if(c==a[1])n=f+4**(m=n),G(x)||(n=m);if(c==a[2])G
(++x)||--x;if(c==a[3])for(;G(x+12);++w)x+=12;if(c==a[4]||c==a[5]){s=sigblock(
8192);printf("\033[H\033[J\033[0m%d\n",w);if(c==a[5])break;for(j=264;j--;Q[j]=
0);while(getchar()-a[4]);puts("\033[H\033[J\033[7m");sigsetmask(s);}}d=popen(
"stty -cbreak echo stop \023;sort -mnr -o HI - HI;cat HI","w");fprintf(d,
"%4d from level %1d by %s\n",w,l,getlogin());pclose(d);}

Ugly as shit.
Assembly language programming should be fun. That's why I do it.

greenozon

Quote from: jj2007 on February 21, 2024, 11:09:08 PMUpdate: attached a rather bloated 37 lines window template, including a Gdi32 demo - enjoy :thup:


how about refactor these 2 lines

invoke BeginPaint, hWnd, addr ps
   mov hDC, eax


into singleliner?

using rv() marco?

greenozon

Also the msg loop is a bit unusual...

usually I'm using this one:

.while TRUE
invoke GetMessage,addr msg,NULL,0,0
  .BREAK .if !eax
invoke TranslateMessage,addr msg
invoke DispatchMessage,addr msg
.endw


jj2007

Quote from: greenozon on March 12, 2024, 08:31:57 PMinto singleliner?

using rv() marco?

Your wish is my command, see attachment :biggrin:

Quote from: greenozon on March 12, 2024, 10:19:03 PMAlso the msg loop is a bit unusual...

.BREAK .if !eax
What happens with your code if GetMessage returns -1?

QuoteGetMessage function
Return value
Type: BOOL

If the function retrieves a message other than WM_QUIT, the return value is nonzero.

If the function retrieves the WM_QUIT message, the return value is zero.

If there is an error, the return value is -1. For example, the function fails if hWnd is an invalid window handle or lpMsg is an invalid pointer. To get extended error information, call GetLastError.

Compare to the code in Windows36lines:
inc rv(GetMessage, addr msg, 0, 0, 0)
shr eax, 1
.Break .if Zero? ; 0 (OK) or -1 (error)

greenozon

jj, nice!!

going back to
>>If there is an error, the return value is -1. For example, the function fails if hWnd is an invalid window handle or lpMsg is an invalid pointer. To get extended error information, call GetLastError.

tell me, from the bottom of your heart,
what is the % of probability in your specific piece of (great) SW that this error might happen?