News:

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

Call MessageBoxW assembly code from C++

Started by austinm, June 01, 2010, 10:07:08 PM

Previous topic - Next topic

austinm

Hi,

First of all, a short introduction since this is my first post on the forum. I'm Mike, a 3rd-year university student studying computer science. I've just recently been taking my first stabs at the Win32 API and its accompanying disassembly, etc.

Currently, I'm trying to put a few lines of assembly into my C++ Windows program. Here is what I have so far:


wchar_t MsgBoxCaption[] = L"Caption";
wchar_t MsgBoxText[] = L"Hello World!";
__asm
{
    push hWnd
    push MsgBoxCaption
    push MsgBoxText
    push 0 ; MB_OK
    call MessageBoxW
}


I am aware of the 'invoke' directive, but I'd like to code it the old-fashioned way to really learn more about what's going on.

Unfortunately, when I build and start debugging, I get this:

Unhandled exception at 0x0129b3f0 in HelloWorld.exe: 0xC0000005: Access violation.

I've done some Googling around, but I'm not quite sure what the problem is. I've tried various syntaxes and also found something about stack memory alignment.

Does anyone see what I'm doing wrong? Thanks for your help.

-Mike

Richard van Petrov

Welcome to the forum Mike!

    push hWnd
    push offset MsgBoxCaption
    push offset MsgBoxText
    push 0 ; MB_OK
    call MessageBoxW


Literally, you are trying to put "Caption" and "Hello World!" onto the stack. What you want to do is: is pass a pointer that points to the data locations.

int WINAPI MessageBox(
  __in_opt  HWND hWnd,
  __in_opt  LPCTSTR lpText,
  __in_opt  LPCTSTR lpCaption,
  __in      UINT uType
);

Richard van Petrov

And I also see that you are mixed up on how to call a procedure with parameters.

When calling a procedure in assembly, the last parameter is pushed onto the stack first, and the first parameter is pushed onto the stack last.


Here is the correct form of your code:
    push 0 ; MB_OK
    push offset MsgBoxCaption
    push offset MsgBoxText
    push hWnd
    call MessageBoxW


Note: When you include the 'windows.inc' file (from your \masm32\include folder) into your code, you can write constants into your code like this:
push MB_YESNOCANCEL or MB_INFORMATION
push NULL ; Puts 'Error' as the title === refer to MessageBox Function documentation
push offset MsgBoxText
push NULL
call MessageBoxW

hutch--

Mike,

Richard is correct here, almost exclusively Windows API calls are STDCALL, push arguments in reverse order and the procedure balances the stack on exit. When working with C/C++ you can also call other functions/procedures written in MASM with the C calling convention, push a variable number of arguments in reverse order to the procedure and the caller balances the stack. The action is in using matching calling conventions between C and MASM and usually using the EXTERN notation with the C compiler to prototype the MASM functions that you call.

Inline assembler still works in Win32 with Microsoft C compilers but with the coming 64 bit code you will need to write seperate modules in MASM for assembler code.
Download site for MASM32      New MASM Forum
https://masm32.com          https://masm32.com/board/index.php

austinm

Hi, thanks for the replies. I remember learning about "left-pushing" and "right-pushing" arguments onto the stack, apparently I had it backwards.

Here is the build output from changing to "push offset MsgBoxCaption" instead of "push MsgBoxCaption":

error C2415: improper operand type

I tried casting the wchar_t pointers to LPCTSTR, but apparently that's not the cause. Am I missing something simple?

hutch--

 :bg

Probably, set the arguments as DWORD as they are all the same size. Just make sure you have a Unicode string for each if you call the unicode version of MessageBox.
Download site for MASM32      New MASM Forum
https://masm32.com          https://masm32.com/board/index.php

GregL

austinm,

You didn't mention the version of the compiler you are using.  Using VC++ 2010 Express this works for me.


    __asm
    {
        push 0    ; MB_OK
lea eax, MsgBoxCaption[0]
push eax
lea eax, MsgBoxText[0]
push eax
push 0    ; hWnd = NULL
        call DWORD PTR MessageBoxW
    }


Don't ask me why you need the DWORD PTR in the call.  ( I copied the compiler output  :wink  ).


Vortex

Pelles C 6 :


__asm{
push 0
push OFFSET title
push OFFSET msg
push 0
call DWORD PTR MessageBoxW
}

GregL

#8
Vortex,

Yes, in VC 2010 it gives "error C2415: improper operand type" for "push OFFSET MsgBoxCaption" or "push OFFSET MsgBoxCaption[0]".  I got errors using ADDR too, had to do it the long way.

I have never really cared for inline assembly, it has a different set of rules from MASM, and it's too limited.  It does have it's uses though.

Richard van Petrov

Mike,

If your code is in a procedure or function then you will need to use 'addr' and not 'offset'

What push addr MessageText does is:
lea eax, MessageText
push eax


You use addr for local variables because their address locations are made at runtime and not compile time.


void ExampleProc()
    wchar_t MsgBoxCaption[] = L"Caption";
    wchar_t MsgBoxText[] = L"Hello World!";
    __asm
    {
        push 0 ; MB_OK
        push addr MsgBoxCaption
        push addr MsgBoxText
        push hWnd
        call MessageBoxW
    }
}

austinm

I got it working!

Thank you all for your replies. I was using Visual Studio 2010 Ultimate.

I was vaguely aware that there were different methods of loading addresses, whether they are defined at run-time or compile-time. Lesson learned to watch more closely for that in the future.

Overall it's been a pretty good first thread, I'm sure I'll be back later with more questions.

Vortex

Hi Greg,

Me too, I don't prefer the inline assembly system. Moving assembly code to .asm files and creating separate object modules is better.