Thursday, November 15, 2007

RichEdit common contrl

i'll keep this post update with all the things about Rich Edit.

#1 The RichEdit Version

The current versions of RichEdit control and their modules are:

ver 1.0 contained in Riched32.dll
ver 2.0 contained in Riched20.dll
ver 3.0 contained in Riched20.dll
ver 4.1 contained in Msftedit.dll

The Rich Edit 3.0 (Riched20.dll) is included in releases of Microsoft Windows 2000 and above.

#2 Initialization
You need to explicitly load the RichEdit library in your application in order to use the control. Hence, to use the 3.0 version call:

if(NULL == LoadLibrary("RichEd20.dll"))
{
// cannot load RichEdit v3.0
}

Open and select a file in windows explorer

Many applications can open an windows explorer window an select a file. This is done by running explorer.exe with some command line switches.

There is a KB with the documented windows explorer switches:

The KB says that the supported versions include old OSes like Windows 95 but I think that 'select' switch for example works only for Windows XP and above.

Here is another KB for XP Windows Explorer.

A solution for older Windows OSes is to use Shell API i think.
I have not tried this but one solution would be to:
call CreateProcess( "explorer.exe" )
enumarate from ROT the windows explorer instances
connect to a instance and use the Shell API to navigate to the file.

Monday, October 08, 2007

smallest EXEcutable

This is an interesting page discussing techniques to achieve the smallest EXE possible.

http://www.phreedom.org/solar/code/tinype/

More, it says that if you add an UNC path as a name of an imported DLL in an executable, Windows will download and execute that file !
the server needs to be a WebDAV server.

Thursday, October 04, 2007

the one and only, the TB_ADDSTRING message !

If you try to use TB_ADDSTRING with the string from the resource then you might had blown your head trying to figure out what the heck is happening, why does it fail:

const int iIndex = ::SendMessage(
hToolBar,
TB_ADDSTRING,
hResource,
IDS_TBSEARCH); // string id from module's resource

iIndex is returned -1 (indicating an error).

Now, I looked at my MSDN from disk and then looked at the online version. Nada. But the old friend Mr. Google saved the day (he always does). I got this page:

The problem with TB_ADDSTRING or the AddString method that is simply
a wrapper to this message is that the first character in the string
resource is used as separator and is replaced with a NULL character.

So your string resource must be something similar to: "|Button text||"
where the pipe character is replaced with '\0'.
This is because the string you pass with TB_ADDSTRING message must
have a double null terminator. You can also use the TB_ADDSTRINGS
message with which you can set all the button texts using a string
like this: "|Button 1|Button 2|Button 3|Button 4||".

This is not well documented or not documented at all, I don't
remember. Anyway keep in mind that the text added to the button with
TB_ADDSTRING is not retrieved using TB_GETBUTTONINFO, but only using
TB_GETBUTTONTEXT, this is why if you use this method instead of using
TB_SETBUTTONINFO, the chevron menu does not display the text of the
menu items


No, it's not documented at all.
Something was fishy from the start though.

When you create the toolbar using the CreateToolbarEx API, you pass an array of TBBUTTON structs. TBBUTTON has its last field either an zero-based index or a pointer to string buffers (the last string needed to be terminated by an extra null to indicate the end of the list).
Zero-based index of the button string, or a pointer to a string buffer that contains text for the button.


But the question is: In the case when you specified in the TB_ADDSTRING a resource string id, what's the zero-based index from TBBUTTON for ?

The problem is that the documentation is incomplete due to what the API tried to achieve. It wanted to provide several ways to add strings but the documention failed to explain exactly.

In toolbar controls you can set buttons text in at least 2 ways:
One way is when you create the toolbar using CreateToolbarEx API.
In each TBBUTTON structure you can put either a straight text buffer or a zero-based index of the button string:

TBBUTTON tbbtn = {0};
...
tbbtn.iString = _T("Text");

or

TBBUTTON tbbtn = {0};
...
tbbtn.iString = 0; // index

The index value is the index from the string list you set to the toolbar control using the TB_ADDSTRING message. The string list can be from 2 sources, either from:

a character array with one or more null-terminated strings:

LPCTSTR szTexts[] = {_T("Text1"), _T("Text2\0") };
::SendMessage(hToolbar, TB_ADDSTRING, NULL, (LPARAM)szTexts);

or from a string in the executable resource:

::SendMessage(hToolBar,
TB_ADDSTRING,
hResource,
IDS_TBSEARCH); // string id from module's resource

but in the latter case the resource string needs to be in a special form. Its first character will be used by the Common controls API as string separator, so
IDS_TBSEARCH can look like this: |Text1|Text2||

In the first case, you can use TB_ADDSTRING at a time, to add each string to the control internal list.

::SendMessage(hToolbar, TB_ADDSTRING, NULL, (LPARAM)_T("Text1\0"));
::SendMessage(hToolbar, TB_ADDSTRING, NULL, (LPARAM)_T("Text2\0"));

Another way to set a toolbar button text is using the TB_SETBUTTONINFO message but you are limited to pass only a string buffer.

Thursday, September 27, 2007

MAKEINTRESOURCE macro

Many Win32 API functions use a LPCTSTR parameter as resource name or type.
For example, a trivial one:

HICON LoadIcon(HINSTANCE hInstance, LPCTSTR lpIconName);

And the documentation for the lpIconName states that:

Pointer to a null-terminated string that contains the name of the icon resource to be loaded. Alternatively, this parameter can contain the resource identifier in the low-order word and zero in the high-order word. Use the MAKEINTRESOURCE macro to create this value.

Now, how the LoadIcon code knows to diferentiate between a resource string name and a predefined constant like IDI_ASTERISK which is defined as:

#define IDI_ASTERISK MAKEINTRESOURCE(32516)

We are used to the wizard generated resource IDs in our applications but a resource can have any null terminated string as name and type too. Look to the FindResource function for example.

All resource Win32API functions use the macro IS_INTRESOURCE(id)

BOOL IS_INTRESOURCE(
WORD wInteger
);

#define IS_INTRESOURCE(_r) (((ULONG_PTR)(_r) >> 16) == 0)

If the macro is TRUE then the id "specifies the integer identifier of the name or type of the given resource. Otherwise, those parameters are long pointers to null-terminated strings"

Practically, the memory address of a name or type passed as input, is checked to see if its within the WORD boundary, or else said if the memory address is between 0 and unsigned short (65536), because:

typedef unsigned short WORD;

So, for the win32 API functions, if the pointer value has an 'invalid address' it's clear that it's rather an id (a integer, an unsisgned short in fact) then an actual memory address containing a null terminated string from the application.

Wednesday, September 26, 2007

Navigating to an embedded resource in .NET

This task is very common but since i did not find a clear and simple solution i decided to write it.

I have a simple C# application which has a webbrowser control and i want to use the res:// protocol to navigate to an embedded resource, a html file from the assembly, like this:

webbrowser.Navigate("res://myApp.exe/Help.htm");

since managed resources are not native ones, adding an html file to the project and select "embedded resource" wont help.
if you open the assembly with a resource editor you won;t see the html resource.
hence, the res:// protocol wont work.

so i wrote this simple console application which accepts as parameters the resource file you want to add.

Usage:
AddResourceManaged.exe ExePath ResFile ResName [ResType]

adds a new resource to an exe file
ExePath - path of the exe
ResFile - path of the file to be added as a resource
ResName - name of the resource
ResType - (optional) type of the resource; if omitted, the application will try to use one of the default resource types
based on the ResFile file extension

Example:
AddResourceManaged.exe myManagedApp.exe Help.htm help.htm 23

it is equivalent to:

AddResourceManaged.exe myManagedApp.exe Help.htm help.htm
because the value of MAKEINTRESOURCE(RT_HTML) is 23

Wednesday, July 25, 2007

AIAB (Asynchronous Invocation Application Block)

i'll edit this post from time to time to update it untill it's finished.

Worker Thread and Service Agent execution.
If an exception is thrown from the SA, the exceeption is logged (using the Exception management mechanism). The default exception publisher logs to System Event log.
This is because the worker thread has a try-catch inside the thread proc. And this makes that the while loop inside the thread procedure to continue.
The Worker thread has a serviceAgentRequest memeber variable which is set \ cleared to null on SA creation execution \ SA execution returns.
Worker thred checks for this variable at the loop begin and resubmits the request (it wont look at the requests queue).

So, when an exception is thrown from a SA during execution, the SA will be resubmitted indefinetely until the SAMonitor aborts the worker thread.

Regarding the SAMonitor: Note that in the case of the exeception thrown from SA, the SAMonitor is called to add the worker thread at each resubmission. But SAMonitor checks if it has that worker thread already in its thread table and removes it and adds it again.

So this means that if a SA exception appears you can decide to either rethrow the exception so the request is resubmit or not rethrow it and maybe just return from execution (and the worker thread will mark the request as completed).

Sunday, July 15, 2007

Opening CHM Help files from Network or Internet

Due to a security patch Windows has restricted access to a CHM. More info here: http://support.microsoft.com/kb/896358.

The attached reg script fixes one of the 4 problems described in the KB. It works for most of the CHM files. Here is the file.


Windows Registry Editor Version 5.00

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\HTMLHelp\1.x\ItssRestrictions]
"MaxAllowedZone"=dword:00000004

Monday, July 09, 2007

Win32 HANDLEs

Each time when you use a win32 API which uses a HANDLE remember that the handle Must be valid (not closed).

It is important to understand the concepts of the handle being signaled and being closed.

Signaled state refers to the handle state. It can be signaled or unsignaled. Wait functions for example, like WaitForSingleObject, block current thread until the object gets signaled.

A handle is said to be closed when its reference count reaches zero value. When this happens the handle is closed and doing any waiting on it is error prone.

A handle is a pointer on an object. Wait functions check the state of the object, hence it is a must that the handler (the pointer) is valid. Otherwise, waiting functions wont work properly (while waiting on an invalid handle, you might wait indefinetly).

One example of bogus use is with MFC AfxBeginThread function. Its default behaviour is to auto 'delete' the thread, i.e to close its handle.
Its bad to use the returned handle after the thread is terminated and closed.
That its not a HANDLE. its a wild HANDLE.

This is described excellent in Chapter 12, Threads in the well known book by Jeff Prosise, Programming Windows with MFC, 2nd Edition.

Another missuse, without MFC, might be in an multithreaded application. One threads create a thread and share the handle others thread which use it.

Thursday, June 21, 2007

Monstrous compilation error on any MFC application

Ok, I've just created a simple MFC DLL, hit the compile button and I got this monstrous error:

c:\Program Files\Microsoft Visual Studio .NET 2003\Vc7\include\excpt.h(36): error C2144: syntax error : 'int' should be preceded by ';'
c:\Program Files\Microsoft Visual Studio .NET 2003\Vc7\include\excpt.h(36): error C2501: 'x' : missing storage-class or type specifiers

And i was like, "WTF !?".
When I see these kind of errors i instantly get several thoughts but in a 'chain of responsability' pattern way:
'i need to check the code again'
'ummm, lemme search google for the error'
'shit, i need to reinstall the vstudio'
'ok, i think i need 'a' beer'

Ive been lucky enough not to reach the last option in my programming problems ive had so far(though, i dont need programming problems to do that, really).

googling a bit i found somebody adviced for a VisualStudio reinstallation. hmmm. too lazy for doing that (and not necessary). but searchin in the microsoft.com for 'C2144' i got a post with a similar issue and an idea that some of the headears might got mistakenly changed by an accidentaly edit.
i said, Ok, no problem i will do a search on windows explorer to see what was changed today.
i did, and to my horrification i found no change.
luckily, i have another pc with a visual studio installation. i quickly opened windiff over the atlmfc folder. and there it was: 'afxv_w32.h'. a smiley 'x' was lying there.

i wasnt able to find the header simply because it was not changed today. it was changed 2 days ago :|

Wednesday, April 04, 2007

Compile libjpeg instructions

http://www.nevrax.org/tikiwiki/tiki-index.php?page=libJPEG

working with DirectShow

The project I am working on has some code already written using DirectShow and I am working with Visual Studio 2005.

DirectShow needs DirectX also.
So you need both of them installed.
It seems that DirectShow was moved from DirectX and was a separate package. Now it seems it is part of the Windows Platform SDK.

Latest "DirectX Software Development Kit" location here

Latest Platform SDK it is called "Microsoft Windows Server 2003 R2 Platform SDK" and get it from here
From the webpage select the type of target platform you want the SDK for.
It will download a small web installer from which you can select what SDK components you want to download an install. In this case choose DirectShow.

The project uses a DirectShow filter and this one needs Strmbase.lib which is the static library from BaseClasses (located under: (SDK root)\Samples\C++\DirectShow\BaseClasses)
The documentation says that there it should be an BaseClasses.dsw file but its not.
I did a search on google on this file and i found this as a response to someone with the same problem:
"There are no project files in the Platform SDK. You must build from command line using the appropriate build shortcut under the Platform SDK menu item.

Once launched, use the DOS CD command to change to the Baseclasses directory and type NMAKE.

You can also get just the project files from an older distribution here:

http://tmhare.mvps.org/downloads.htm"

So I downloaded "DirectShow SDK Project Files (version 0.2 for W2K3 R2 PSDK) 146 KB." from that location which has the sln and vcproj files to build BaseClasses.

Also, when building DirectShow applications make sure that:

in Visual Studio VC++ Directories Options you have at the top both in Include and Libs the path to the Include and Lib folders of DirectShow and DirectX.

if the application (for example a filter) uses environment variables to locate the DirectShow include \ library files (like $(DSHOWBASECLASSES)\.,$(DXSDK)\include) then make sure you have or define else these variables.
$(DSHOWBASECLASSES) must point to the (SDK root)\Samples\C++\DirectShow\BaseClasses
where (SDK Root) is the root folder installation of DirectShow (the application (filter) needs the path to locate strmbas.lib file (the BaseClasses lib))

$(DXSDK) is the root folder of the DirectX installation.

If while compiling you get this error:
error C1189: Need to include strsafe.h after tchar.h

A possible explanation is done here: http://harshdeep.wordpress.com/2006/05/30/directshow-with-visual-studio-8/
(he tried to compile by hand the BaseClasses and link to it in his application and he got different errors)

"The first step was to get the latest DirectShow SDK - as I mentioned in the last post, it’s a part of the Platform SDK and Direct X SDK is required to build the samples. The first sample, Baseclasses, is what I needed. I built the static libraries strmbasd.lib (debug) and strmbase.lib (release) through nmake, as DirectShow documentation suggested, and linked them to the corresponding builds of my dll that uses DirectShow. This opened up a Pandora’s box of compiler, linker and run-time errors.

tchar.h was throwing a compiler error - “error C1189: Need to include strsafe.h after tchar.h”. Lots of people have complained about this error in the forums. dshow.h includes strsafe.h, so in this case the error simply reads “include tchar.h before including dshow.h”.

I was getting some “undefined symbols” link errors for functions that I could clearly see in Baseclasses code. It was because I’d used nmake to build the libraries. The default settings that it used were not compatible with the project settings of my DirectShow code.

I created a VS8 project for Baseclasses. All I had to do to fix the link errors was to match the “Character Set” and “Treat wchar_t as a Built-in type” settings in the project properties of Baseclasses and my dll that links to it. But this code was still crashing. Reason - different “Structure alignment” in the two projects. Using the same value in both the projects fixed it.

Moral of the story: Don’t use nmake to build Baseclasses for you - write a VS project and use the right settings.
"

Saturday, March 24, 2007

CreateDialog, CreateWindow or any other window\dialog creation function fails and GetLastError is 0

... then check if you have called InitCommonControls() at very start of your application if you use any common controls (list view, tree view ,etc)

Thursday, March 01, 2007

ASSERT, ASSERT and ASSERT. Also, always ASSERT !

Putting assertions in your code was always VERY important.
I constantly find its importance in my coding.
It is very important to always assert your variables\conditions along with the testing it ( 'if' statement ).
Also, another common use is in an algorithm. it is important to assert different conditions which might define a state at a moment, a state which I know it should be false\true (when i mean a state i mean a condition composed of multiple variables, variables which might not be in the same scope but I expect some specific values).
i am having situations where while coding, i come accross some asserts which make me say "wtf, how did that happen ?" and then i discover that the algo has some issues which were not obvious due to complexity (or my fatigue).

Friday, February 16, 2007

mozilla build from source code

1. get the mozilla source code
--------------------------------------------------
Get source code from
https://developer.mozilla.org/en/Download_Mozilla_Source_Code

Copy the firefox-1.5-source.tar.tar in e:\
Create a directory called e:\mozilla
Extract the firefox-1.5.source.tar.tar into mozilla directory

2. install cygwin
--------------------------------------------------
run http://www.cygwin.com/setup.exe

choose the first type of install (the first form with the 3 radio-buttons) and choose "DOS" style line endings

as you read the http://developer.mozilla.org/en/docs/Windows_Build_Prerequisites you see that
you need to make sure that the cygwin packages include these:

ash -- UNIX-like command line interpreter shell (Base category)
coreutils -- GNU core utilities (includes fileutils, sh-utils, and textutils) (Base category)
diffutils -- file comparison utility (Base category)
findutils (Base category)
gawk -- pattern matching language (Base and Interpretors categories)
grep -- text search tool (Base category)
make -- dependency analyzer for software builds (Devel category)
patchutils -- a small collection of programs that operate on patch files (Devel category)
perl -- a scripting language used to control parts of the build (Interpreters category)
sed -- a search and replace language (Base category)
unzip -- zip file extraction (Archive category)
zip -- zip file creation (Archive category)

( u need to click if the package is marked as Skipped and choose latest version (some packages have multiple versions))


3. setup environment variables
--------------------------------------------------
create a file called 1.bat near the cygwin.bat, in c:\cygwin
put the followin content in 1.bat (attention to the VS.net paths and others if necessary)

@echo off
rem --- Basic config, with VC7 libIDL files
SET MOZ_TOOLS=C:\moztools
SET GLIB_PREFIX=C:\vc71
SET LIBIDL_PREFIX=C:\vc71
SET MINGWBASE=C:\Dev-Cpp
SET CYGWINBASE=C:\cygwin

rem --- Clean slate start
SET INCLUDE=
SET LIB=
SET PATH=C:\;C:\windows\system32;C:\windows;C:\windows\system32\wbem


rem --- Set VC7 compiler environment vars
CALL "C:\Program Files\Microsoft Visual Studio .NET 2003\Vc7\bin\vcvars32.bat"

rem --- Then prepend Cygwin path
SET PATH=%CYGWINBASE%\bin;%PATH%

rem --- Add glib/libidl to build environment
SET PATH=%PATH%;%GLIB_PREFIX%;%GLIB_PREFIX%\bin
SET INCLUDE=%GLIB_PREFIX%\include;%INCLUDE%;%MINGWBASE%\include
SET LIB=%GLIB_PREFIX%\lib;%LIB%

rem -- moztools comes last after glib/libIDL
SET PATH=%PATH%;%MOZ_TOOLS%\bin


4. edit the c:\cygwin\cygwin.bat
--------------------------------------------------
add the following line at the beginning of the cygwin.bat content

call 1.bat

cygwin.bat should now like this:

@echo off

call 1.bat

C:
chdir C:\cygwin\bin

bash --login -i

5. create a folder called C:\vc71
--------------------------------------------------
get the http://ftp.mozilla.org/pub/mozilla.org/mozilla/source/wintools.zip
and extract the content in c:\vc71

only 3 dirs are needed: bin, include and lib
so the content of c:\vc71 should be:
c:\vc71\bin
c:\vc71\include
c:\vc71\lib

this dirs are the
SET GLIB_PREFIX=C:\vc71
SET LIBIDL_PREFIX=C:\vc71

from the 1.bat file

also, read the 'Netscape wintools' section from http://developer.mozilla.org/en/docs/Windows_Build_Prerequisites
install.bat needs to be run

6. create a file called 'mozconfig' in E:\mozilla with the following content:
--------------------------------------------------
mk_add_options MOZ_CO_PROJECT=all
ac_add_options --enable-application=browser
ac_add_options --disable-optimize
ac_add_options --enable-debug

this is taken from: http://developer.mozilla.org/en/docs/Configuring_Build_Options
read the instructions



7 build it !
--------------------------------------------------
run the cygwin.bat from c:\cygwin
make sure that at the top you see some comments from the vsvars32.bat (this means the environment variables are set)

type:
make -f client.mk build_all

in order to do a cleanup after a build, type
make -f client.mk clobber_all

if errors are encountered, use the http://developer.mozilla.org/en/docs/Mozilla_Build_FAQ

for example, it is possible that this error to appear (at least i got it):
configure: error: the linker major version, , does not match the compiler suite version, 6.
The cygwin tool "link.exe" is being confused for an object linker.
Make sure that the Microsoft tools are before cygwin in your PATH, or rename or remove /bin/link.exe

as it says i needed to rename the c:\cygwin\link.exe to link__.exe

Thursday, February 15, 2007

threading on GDI objects

When you dealing with multithreading in your code, every data you use in more than one thread it should be a subject to your attention in the way its used in those threads.

Similary, using GDI objects in multithreading does not require any special code beside some (natural) common-sense 'not to do' things, which is, dont read and modify un-synchronized in the same time. Also, remember that they have thread affinity (the thread which created the object should be the one who deletes it).

The following text is from the Raymond Chen wonderful blog:

Window objects: thread which created the window its said to be the window 'owner'.

Messages are dispatched to a window procedure only on the thread that owns it, and generally speaking, modifications to a window should be made only from the thread that owns it. Although the window manager permits any thread to access such things as window properties, styles, and other attributes such as the window procedure, and such accesses are thread safe from the window manager's point of view, load-modify-write sequences should typically be restricted to the owner thread. Otherwise you run into race conditions such as the following:

wpOld = (WNDPROC)GetWindowLongPtr(hwnd, GWLP_WNDPROC);
SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR)newWndProc);
LRESULT CALLBACK newWndProc(...)
{
... CallWindowProc(wpOld, ...); ...
}
If modifications to the window procedure are made carelessly from any thread, then between the first two lines, a second thread may change the window procedure of the window, resulting in newWndProc passing the wrong "previous" window procedure to CallWindowProc.

Why, then, does Windows even allow a non-owner thread from changing the window procedure in the first place? Because, as we all know, 16-bit Windows was a co-operatively multi-tasked system, which means that one thread could do anything it wanted secure in the knowledge that no other thread would interrupt it until it explicitly relinquished control of the CPU. Therefore, the above code sequence was safe in 16-bit Windows. And for compatibility reasons, the code continues to be legal, even though it isn't safe any more. (Note, however, that in an attempt to limit the scope of the damage, the window manager allows only threads in the process that owns the window to change the window procedure. This is a reasonable limitation since separate address spaces mean that function addresses in other processes are meaningless in the process that owns the window anyway.)

Device contexts:

Device contexts (DCs) also have a certain degree of thread affinity. The thread that calls functions such as GetDC must also be the one that calls ReleaseDC, but as with window handles, during the lifetime of the DC, any thread can use it, but one at a time. If you choose to use a DC in a multi-threaded manner, it's your responsibility to coordinate the consumers of that device context so that only one thread uses it at a time. For example, to host windowless controls across multiple threads, the host obtains a DC on the host thread, then asks each control in sequence to draw itself into that DC. Only one control draws into the DC at a time, even if the control happens to be on a different thread.

Menus:

Menus do not have thread affinity. Any thread can use a menu. However, if two threads use a menu, it is the responsibility of those threads to coordinate among themselves how that menu will be used, so that one thread doesn't modify a menu while another is busy displaying it, for example.


Icons, cursors, and accelerator tables behave like menus. They do not have thread affinity. They are easier to manage than menus since they cannot be modified once created, so the only thing you have to worry about is not to use one after it has been destroyed

GDI objects are much simpler. As a general rule, they all have process affinity: They can be used by any thread in the process that created them. If you use a GDI object from multiple threads, it is your responsibility to coordinate the object's use.
Note that the window manager and GDI as a general rule keep their respective objects thread-safe. When I say that it is your responsibility to coordinate an object's use from multiple threads, I mean that you have to coordinate among your own threads if you're going to modify the object from one thread and read from it on another or modify it from two threads. For example, if one thread enumerates a menu while another is modifying it, the one doing the enumeration will get inconsistent results. Similarly, if two threads both try to change a menu item at the same time, the last writer will win.

http://blogs.msdn.com/479124.aspx

http://blogs.msdn.com/479587.aspx

http://blogs.msdn.com/480064.aspx

http://blogs.msdn.com/480569.aspx

http://blogs.msdn.com/481043.aspx

Monday, January 08, 2007

IHTMLFormElement::get_elements fails


Don't blow your head, you have this situation:
you have a valid form element and you want to access its elements.

IHTMLFormElement* pForm = ...

one way to do it is by:

HRESULT IHTMLFormElement::get_elements(IDispatch **p)

and the code looks like the following:

IDispatch* pDisp = NULL;
pForm->get_elements(&pDisp);


IHTMLElementCollection* pFormColl = NULL;
HRESULT hr = pDisp->QueryInterface(IID_IHTMLElementsCollection, (void**)&pFormColl);

but you get pFormColl having NULL value and hr = 0x80004002 which means

The QueryInterface method did not recognize the requested interface. The interface is not supported.

Google'ing it a bit you get plenty of similar behaviour. The workaround is to use

HRESULT IHTMLElement::get_children(IDispatch **p)

The hint is given by MSDN description for this method:

To access all elements contained in a form, call QueryInterface on IHTMLFormElement and request an IHTMLElement interface. Use the IHTMLElement::children property of the IHTMLElement interface to retrieve a collection of all elements in the form

So, translated into code:

IHTMLElement* pElem = NULL;
pFormElem->QueryInterface(IID_IHTMLElement, (void**)&pElem);

IHTMLElementCollection* pChildrenElem = NULL;
pElem->get_children(&pChildrenElem);


After all, this should be your preferred way since MSDN also says that you do not get actually all the form elements if you use IHTMLFormElement::get_elements :

The collection retrieved by the IHTMLFormElement::get_elements property does not include input type=image elements from a form

So, in order to obtain a IHTMLFormElement children elements, use IHTMLElement method instead.