News:

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

Main Menu

Implementation of the COM interface FileOpenDialog

Started by jorgon, February 19, 2025, 11:31:58 AM

Previous topic - Next topic

jorgon

This was a reference to our being spared this sort of stuff as assembler programmers.

This is from Microsoft's "COM Coding Practices" article here: https://learn.microsoft.com/en-us/windows/win32/LearnWin32/com-coding-practices:

QuoteWhen you build your program, you might get linker errors similar to the following:

unresolved external symbol "struct _GUID const IID_IDrawable"

This error means that a GUID constant was declared with external linkage (extern), and the linker could not find the definition of the constant. The value of a GUID constant is usually exported from a static library file. If you are using Microsoft Visual C++, you can avoid the need to link a static library by using the __uuidof operator. This operator is a Microsoft language extension. It returns a GUID value from an expression. The expression can be an interface type name, a class name, or an interface pointer.

Using __uuidof, you can create the Common Item Dialog object as follows:

IFileOpenDialog *pFileOpen;
hr = CoCreateInstance(__uuidof(FileOpenDialog), NULL, CLSCTX_ALL,
    __uuidof(pFileOpen), reinterpret_cast<void**>(&pFileOpen));
The compiler extracts the GUID value from the header, so no library export is necessary.

We saw that both CoCreateInstance and QueryInterface require coercing the final parameter to a void** type. This creates the potential for a type mismatch. Consider the following code fragment:

// Wrong!

IFileOpenDialog *pFileOpen;

hr = CoCreateInstance(
    __uuidof(FileOpenDialog),
    NULL,
    CLSCTX_ALL,
    __uuidof(IFileDialogCustomize),       // The IID does not match the pointer type!
    reinterpret_cast<void**>(&pFileOpen)  // Coerce to void**.
    );
This code asks for the IFileDialogCustomize interface, but passes in an IFileOpenDialog pointer. The reinterpret_cast expression circumvents the C++ type system, so the compiler will not catch this error. In the best case, if the object does not implement the requested interface, the call simply fails. In the worst case, the function succeeds and you have a mismatched pointer. In other words, the pointer type does not match the actual vtable in memory. As you can imagine, nothing good can happen at that point.

 Note

A vtable (virtual method table) is a table of function pointers. The vtable is how COM binds a method call to its implementation at run time. Not coincidentally, vtables are how most C++ compilers implement virtual methods.

The IID_PPV_ARGS macro helps to avoid this class of error. To use this macro, replace the following code:

__uuidof(IFileDialogCustomize), reinterpret_cast<void**>(&pFileOpen)
with this:

IID_PPV_ARGS(&pFileOpen)
The macro automatically inserts __uuidof(IFileOpenDialog) for the interface identifier, so it is guaranteed to match the pointer type. Here is the modified (and correct) code:

// Right.
IFileOpenDialog *pFileOpen;
hr = CoCreateInstance(__uuidof(FileOpenDialog), NULL, CLSCTX_ALL,
    IID_PPV_ARGS(&pFileOpen));
You can use the same macro with QueryInterface:

IFileDialogCustomize *pCustom;
hr = pFileOpen->QueryInterface(IID_PPV_ARGS(&pCustom));

Villuy

If compiler not works as it should, then creators of compiler should make it work as it should.

No?

NoCforMe

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

Villuy

Quote from: NoCforMe on February 22, 2025, 04:08:04 PMDefine "as it should".
See the problem?

Compiler VC++ has system problem which its creators are not able to solve. But OP has bypassed problem. Correct?

NoCforMe

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

Villuy

So what C++ has to do with it at all, if thread is about GoAsm?

zedd151

Quote from: Villuy on February 22, 2025, 07:35:23 PMSo what C++ has to do with it at all, if thread is about GoAsm?
I can at least partially answer that. All of the documentation that Microsoft puts out for any of the Windows API's is mostly (entirely?) geared toward and written for C/C++ programmers. Also the source header files (*.h) are written for C/C++, the Microsoft variety, and it is left up to the assembly programmer to convert the C/C++ header files to assembly compatible include files (*.inc), either manually or via a third party or self written tool.

Therefor any discussion of using Microsoft Windows API's for assembly will, at some point, include discussing (Microsoft) C/C++ data types for structures, etc... Assembly language programmers are not bound to the assumptions/limitations imposed by MSVC/MSVC++ as far as data types, but must at least be familiar with those data types, in relation to assembly programming to properly make the conversion. :smiley:

At least that's my take on this...
¯\_(ツ)_/¯

TimoVJL

Good point  :thumbsup:
We wont see any MS documentation in C anymore, just in C++
May the source be with you

sinsi

Quote from: zedd151 on February 22, 2025, 11:26:16 PMTherefor any discussion of using Microsoft Windows API's for assembly will, at some point, include discussing (Microsoft) C/C++ data types for structures, etc... Assembly language programmers are not bound to the assumptions/limitations imposed by MSVC/MSVC++ as far as data types, but must at least be familiar with those data types, in relation to assembly programming to properly make the conversion. :smiley:
+1

Before x64 it used to be "everything is a DWORD"  :angelic:

jj2007

Quote from: sinsi on February 22, 2025, 11:47:24 PMBefore x64 it used to be "everything is a DWORD"

Nowadays it's "either a pointer or a handle, or a DWORD" :biggrin:

zedd151

¯\_(ツ)_/¯

sinsi

Quote from: jj2007 on February 22, 2025, 11:54:10 PM
Quote from: sinsi on February 22, 2025, 11:47:24 PMBefore x64 it used to be "everything is a DWORD"

Nowadays it's "either a pointer or a handle, or a DWORD" :biggrin:
My combined 32/64 bit windows.inc starts with
IFDEF __WIN64__
    _align_ = 8
    HANDLE     typedef qword
    POINTER    typedef qword
ELSE
    _align_ = 4
    HANDLE     typedef dword
    POINTER    typedef dword
ENDIF
:badgrin: