GetClassInfo() returned invalid WndProc when putting manifest in my application...

I read all you have written about manifest here ... but find no solution to resolve my problem!!!

Here is the situation :

I work on a big application where the main application is written in VB6 ... This VB6 application call method of some home made DLL written in C++ using VC6 ...

I want that my application and all DLL show XP theme GUI ... So I call InitComCtrl method in the Form_Initialize of my VB6 application ... and I add a manifest (MyAppName.exe.manifest) in the same directory of my exe file.

When I start my Application ... All seems to work fine and GUI is XP themed ...

And then the application crash for no reason!! I debug that, and found that a DLL Call the method GetClassInfo and the call succeed (return TRUE) but it return the WNDCLASS lpfnWndProc member set to 0xffff04df... That is an invalid address!!!

So the crash occur because this DLL Subclass a ListBox Window and try to forward message to the original WndProc returned in the WNDCLASS lpfnWndProc ... That is invalid...

I not that the GetClassInfo method only return invalid WinProc address when the manifest is present!!

Somebody understand something in that??
Some body could help me??
clemexAsked:
Who is Participating?
 
pjasnosCommented:
I think that the best solution would be to change the way your code is hooking Listboxes. :-)

I found this example on the net:

http://www.gipsysoft.com/articles/global_subclass/
The guy is subclassing listbox class as well (well, it modifies the class), but in a slightly different way. I have checked whether it works with xp theming, and it does not crash on my computer. Maybe try this approach as a template for modifying your code.

The crucial part from the above website:

BOOL EnhanceLISTBOX( HINSTANCE hInstance )
//
//  This is the main API. Call it once and that's it!
{
  if( !g_wpOld )
  {
    HWND hwnd = ::CreateWindow( _T("LISTBOX")
        , NULL, 0, 0, 0, 0, 0, 0, 0, hInstance, 0 );
    ASSERT( hwnd );
   
    g_wpOld = reinterpret_cast< WNDPROC >( ::GetClassLongPtr( hwnd, GCLP_WNDPROC ) );
    ::SetClassLongPtr( hwnd, GCLP_WNDPROC, (LONG_PTR)WndProc );

    VERIFY( DestroyWindow( hwnd ) );
    return TRUE;
  }
  return FALSE;
}
0
 
itsmeandnobodyelseCommented:
The lpfnWndProc is a (static) function pointer. If you call it by a DLL function it will fail in case the function pointer is defined from the app. If you subclass a control by the dll you would need to exchange the WindowsProc by a dll exported function as far as I understand. That is the way active-x controls work. They create their control with functions defined in the dll (ocx) only.

The .manifest is a means to get registry information both from registered app and from registered dll out of the .manifest and not from registry. To make that work both the app and the dll need to have self-register functionality which was up-to-date when creating the .manifest. Moreover, if some other dlls or controls were used by either the app or the dll, these versions of the dlls or ocx' must be available at the target machine as well what may be difficult to guarantee in any case. Moreover, as far as I know neither VB6 nor VC6 were made to use .manifest files. The manifest is a feature that came with .NET and works for XP and Vista only. I never heard that it is possible to let VC6 applications work with .manifest though it may work envertheless. How did you create the .manifest files? Couldn't you upgrade to VC7.1 or  VC8?

Regards, Alex
0
 
clemexAuthor Commented:
You are wrong!! You can use manifest with VC6 and VB6 Apps by putting the manifest in resource or in the same directory of the exe file... And this work perfectly ...

About my problem : I read what you write and I don't really understand your point!! A WindowsProc method is a static function ... And the DLL is a part of the process so you can call WinProc from a DLL without problem ... I really don't understand your point ...

More details about my problem:
The Only thing I explained in my question is :  We have a VB6 app, This VB6 app calls an exported function of a DLL I created in VC6... The only thing the exported function, of the DLL, do is calling the GetClassInfo(...) Win32 API function...

So the GetClassInfo(NULL, "LISTBOX", &aWndClass) function fill a WNDCLASS ... And all is ok here!!

After that ... (and not that all is working correctly) the only thing I do is to put a manifest associated to the VB6 App executable... At this point the app is fully XP themed ... But when the app call the same exported function of my DLL the GetClassInfo(NULL, "LISTBOX", &aWndClass) Win32 API function succeeded by returning TRUE but fill the WNDCLASS structure with invalid values... For Example : the lpfnWndProc member of the WNDCLASS struct is equal to somthing like 0xFFFF0DE4 which is an invalid Default LISTBOX WinProc Address...

What do you think??

My Question is Why the Win32 function GetClassInfo(...) does not work correctly when putting manifest  in my app ???

0
Ultimate Tool Kit for Technology Solution Provider

Broken down into practical pointers and step-by-step instructions, the IT Service Excellence Tool Kit delivers expert advice for technology solution providers. Get your free copy now.

 
pjasnosCommented:
lpfnWndProc is correct - this is because there are two kinds of window procedures: client-side (so called normal one) and server side(which one is executed inside kernel address space). This allows to put some common window procedures within kernel, and hence avoid cost of many context switches when doing some more complex drawing. In case window procedure is kernel-side, GetClassInfo returns a "handle" to server side window procedure. This handle is not a valid address - it is only meaningful to kernel counterpart of CallWindowProc. So, assuming you are subclassing by replacing window proc and then calling directly the original one, the solution is to use CallWindowProc instead. This is the way of subclassing recommended by Microsoft.
0
 
clemexAuthor Commented:
Hmm ... I think you're right ... We should use the CallWindowProc API...

I check the sub classing code we have in our system an after subclassing the WinProc of a ListBox the system generate a Access Violation when calling the macro FORWARD_WM_NCCREATE on the WindowProc ...

Hmmm ....

Their is one think I don't understand about you explanation ... Why Microsoft macros don't use the CallWindowProc API the call WindowProc???

The macro FORWARD_WN_NCCREATE is a macro defined by Microsoft in the WindowsX.h file...

And this macro si define like the following:

FORWARD_WM_NCCREATE(hwnd, lpCreateStruct, fn) \
    (BOOL)(DWORD)(fn)((hwnd), WM_NCCREATE, 0L, (LPARAM)(LPCREATESTRUCT)(lpCreateStruct))

This Microsoft macro call the WindowProc directly??

I change the code by replacing the call to FORWARD_WN_NCCREATE call by a call to CallWindowProc(g_WWSWndProc, hwnd, WM_NCCREATE, (WPARAM)0, (LPARAM)lpCreateStruct);

Which is supposed to be correct but the call do an Access Violation because it try to access the address 0xFFFF0DE4 which is the address that GetClassInfo returned ...

Hmmm ... So it seems that CallWindowProc try to call the addess of the WindowProc directly and Freeze because the 0xFFFF0DE4 is not a valid WindowProc Handle an/or function address...

What do you think??  
0
 
pjasnosCommented:
You should probably ask MS why they don't use the Callwindowproc (using it is recommended on their website).

You said you are subclassing a ListBox window - why are you using GetClassInfo to get previous address of window proc?
Solution recommended by MS is:

"To subclass an instance of a window, call the SetWindowLong function and specify the handle to the window to subclass the GWL_WNDPROC flag and a pointer to the subclass procedure. SetWindowLong returns a pointer to the original window procedure; use this pointer to pass messages to the original procedure. The subclass window procedure must use the CallWindowProc function to call the original window procedure.

Note  To write code that is compatible with both 32-bit and 64-bit versions of Microsoft Windows, use the SetWindowLongPtr function."

from http://msdn2.microsoft.com/en-us/library/ms633570.aspx

It *could* be possible that the constant returned (I don't believe it to be an address) may be used by the theme api - they may be intentionally causing the exception and look for this value to do their job (with which you are interfering) <-- this are my guesses only. They may for instance intercept window creation in this way and then replace window procedure - that's why you should not use GetClassInfo. Try with SetWindowLong - if it doesn't work, I'll investigate further.

Fingers crossed

Pawel
0
 
itsmeandnobodyelseCommented:
As the problem arises with the .manifest only, the reasons for the wrong WindowsProc cannot explained by code which runs for both cases. The differences when using a manifest is that you may use different versions of MFC dll or runtime dlls. Also, different active-x controls might be loaded when using a manifest. If a VC6 exe loads a different dll than that it was developed with all kind of thinkable corruption can occur.

To find out whether I am right you should run the depends which was in VC6 tools both when running with manifest and without and check whether the same dlls were loaded. If not, most probably that is the reason for the crash.
0
 
clemexAuthor Commented:
Ok ... I check the code and I find that the code does not override a LISTBOX ... The code create a new Window Class based on a the class LISTBOX...

So the code get the LISTBOX class info by calling GetClassInfo(..., "LISTBOX", ...) and use this info to create a new Window  based on the WNDCLASS returned by GetClassInfo...

The code change the WinProc returned by GetClassInfo, by our own WinProc ... And we keep the original WinProc returned by the GetClassInfo to call the default ListBox WinProc in our own WinProc to let the default implementation to work ... Understand?

This code is really strange ... because It create a new Window Class that work like a List box rather than sub classing a list box ... hmmm...

May be this code is not correct but we have to deal with it ... This code is in the old part of our system and probably be written 10 years ago!! so!!

What do you think?
0
Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.

All Courses

From novice to tech pro — start learning today.