Stubs Partially Explained

From Wine-Wiki

Jump to: navigation, search

Wine sometimes it uses what is called a stub. This topic contains a brief and incomplete primer to stubs and Wine. It is not an indication of how this example function should be written, but merely an primer or introduction to stubs.

The official Wine Wiki has a page which covers writing stubs which would be the first place to look.

Contents

What is a Stub?

What does this stub message: "fixme:actctx:CreateActCtxW stub" mean?


"fixme:actctx:CreateActCtxW stub" means that while Wine has the function CreateActCtxW, the "stub" message means that there is nothing in the function that does anything. The function will get called but (typically) won't do anything other than print a debugging message.

We can investigate using the web interface to the WineHQ CVS Repository. Link. Here is a copy of the relevant code that generates "fixme:actctx:CreateActCtxW stub" (as of March2003).

 /***********************************************************************
 47  * CreateActCtxW (KERNEL32.@)
 48  *
 49  * Create an activation context.
 50  */
 51 HANDLE WINAPI CreateActCtxW(PCACTCTXW pActCtx)
 52 {
 53   FIXME("stub!\n");
 54   return INVALID_HANDLE_VALUE;
 55 }
 56 
 57 /***********************************************************************

Oddly enough, many programs will continue to operate okay with a remarkably large number of stubs in the Wine code, but in some cases, it does have a bad effect.

J. Hawkins [Apr 06]: Most stub API in wine return TRUE/success, because many apps just check that the function succeeds and might not depend on the actual outcome of the API. wine archive

When a stub gives trouble

Normally when this occurs you can try a substitute Windows dll by copying it to the fake_windows system directory. Unfortunately with this example, the function is in the kernel dll (kernel.dll) which must not be swapped. About the only thing that can be done is to write some code to at least partially implement the function. Search via Google for information on this dll and look through the code for other examples which may assist you. If you are hesitating to take this step, dont worry too much - it is rare that you need to implement the whole dll.

update: D. Clark: [Dec 05] Wine recently changed from using system to system32 as the default directory for DLLs.

There are a couple of donts:

  • Dont go looking at Reactos code as A. Julliard has ruled [Jun 06]: No ReactOS-derived code will be accepted in Wine at this point.
  • Dont try reverse engineering Windows by looking at assembly.

M. McCormack [Jun 06]: [You dont need to look at assembly] to figure out that you can just return FALSE/0/NULL and make the program or dll that's calling this function happy. If that doesn't make it happy, you could always write a test program that calls the function [in Windows] and prints out the return value.

D. Timoskov: Read MSDN,books, find code samples on the net, write the tests on your own.

D. Riekenberg cautioned: MSDN is not complete and not Error-Free. [...]Do not forget the Oldies [Books] (Oldies are goldies!): They do not depend on MFC / DotNet / C# wine archive


Continuing with our example: After examining the function QueryActCtxW() in the same file the comment about Adobe Photoshop 7 could give us a clue:

/***********************************************************************
161  * QueryActCtxW (KERNEL32.@)
162  *
163  * Get information about an activation context.
164  */
165 BOOL WINAPI QueryActCtxW(DWORD dwFlags, HANDLE hActCtx, PVOID pvSubInst,
166                          ULONG ulClass, PVOID pvBuff, SIZE_T cbBuff,
167                          SIZE_T *pcbLen)
168 {
169   FIXME("stub!\n");
170   /* this makes Adobe Photoshop 7.0 happy */
171   SetLastError( ERROR_CALL_NOT_IMPLEMENTED);
172   return FALSE;
173 } 

After confirming you have backups of your work, you might then (bravely) try adding before the return in the CreateActCtxW function this line

SetLastError( ERROR_CALL_NOT_IMPLEMENTED);


Should I submit a patch that is only a stub? If it doesn't actually do anything is this ok?

M.Hearn please do. Better stubs are definitely improvements, even if they aren't implementing the functionality.

H. Verbeet [Apr 06]: you shouldn't use MSDN for anything more than getting a general idea of what a function is supposed to do in the first place.It is particularly bad for things like return codes, refcounts and whether things are checked for NULL / written with NULL on error, to name a few things. wine archive

Dont forget to update Wine First
Before you start to try your hand at code, you may want to update to the latest cvs and check the Developer Wiki. Often you can contact developers with the Wine-Devel mailing list. Otherwise occasionally you will find it has been already fixed. In a Wine-Devel post June 2005, one programmer noted: I have already sent a fix (applied) for that 3 weeks ago. You should have updated your tree before sending a patch. ;-)


I cant fix it, but I think I found the error - What can I do to help?
D. Timoshkov: A test case showing how Windows behaves in such a case would be the very first step towards a proper fix.

Example test Case

A User [Sept 05] Noticed: When doing A->W WM_GETTEXTLENGTH, [if I] use WM_GETTEXT behind the scenes to obtain an exact length, this seems to match the behaviour of recent Windows and as well fixes a couple of regressions (caused by theming suddenly making the standard controls Unicode). Any comment on this one? I mean, getting Delphi apps to work again would be nice after all...

A. Julliard: Well, I'm not sure doing a SendMessage is the right thing if you are called from inside CallWindowProc, you may have to do a CallWindowProc instead. I think a test case will be needed to find out what Windows does.

He was asked: how I could determine with what facility(ie CallWndProc() vs SendMessage() vs something else?) a message (in this case, WM_GETTEXT) was sent to a window.

A. Julliard: Do a CallWindowProc on a function that is not the current winproc ofthe specified window and check if the message gets to the window. [A patch was accepted shortly afterward] Wine Archive

  • Further Reading
  • Coding_Hints:Test_Cases

Using Wine to examine a Windows Dll

A User looking at implementing shell32.dll SHHelpShortcuts_RunDLL, asked how he could find out more information about the parameters

  1. HWND hwnd
  2. DWORD dwArg2
  3. LTCSTR szCommand / LPCWSTR wszCommand
  4. DWORD dwArg4

D. Timoshkov pointed out a handy way of troubleshooting: I think that answers to most of the above questions you could find by taking rundll32.exe from win2k or XP, feeding all kinds of different arguments and running it under Wine with +relay,+snoop trace.

Google(-groups) knows something as well: rundll32.exe shell32.dll,SHHelpShortcuts_RunDLL PrintersFolder Wine Archives

Further Reading

When to crash

A programmer noticed that in one particular Windows API, it didnt check for null pointers and asked what should be done for wine...

F. Gouget [Mar 06]: This has been discussed many times before (but maybe not in the recent past). There are two cases to consider:

  • either some applications do pass NULL thus causing a segfault and then expect to catch that exception. As you said, this is pretty unlikely here but it has happened with other (non-DirectX) APIs. In this case, then Wine must segfault too otherwise the application will fail.
  • or the application does not pass a NULL here because it would cause it to crash on Windows. But if we merely return an error the application will merrily go on, only to crash or report that DirectX does not work a while later. By then the source of the problem will be very hard to find due to all that happened since this function call.

The point is that we need to fix the bug that causes the application to pass a NULL to Wine in the first place. And the best way to spot it, and thus fix it, is if Wine segfaults in the same way as Windows when given a NULL.

So segfaulting like Windows does is really the right thing to do. It may warrant a comment in the code though, especially if there is a test with a message right before. wine archive

A. Mohr: We're not a library. We're a very, very, very, very specific piece of software that is required to absolutely, positively fully emulate Windows to the closest extent possible (within practical limits, of course). As such debating whether to delay a crash by adding useless checks *that do not exist in Windows*(!) to *work around* strange behaviour most likely caused by our own non-conformant code is utterly pointless. If there's a problem that we need to be aware of, then we'd better get to know about it NOW, not 5 lines, not 3000 relay lines and not 10 minutes after it occurred and nobody ever remembers what the actual problem was.

A. Julliard: A good library (which the MS ones certainly aren't...) should crash on bad pointers, so that bugs can be found and fixed. Hiding bugs and trying to stumble along is the MS way, this is what leads to idiocy like having an exception handler in lstrlen(). So if in this case Windows doesn't have a check we certainly don't want one either. [...dont] assume that the app will handle the error in a sane way. That's extremely unlikely, especially for things like bad pointers since the app obviously didn't expect to be passing bad pointers. If the app really wants to handle that sort of error it can always add an exception handler around the call; so not trapping the error in the library doesn't prevent the app from catching the error, it just avoids forcing a lot of useless checks on well-written apps. And it's not clear at all that crashing is more likely to lose data than not crashing. With the Windows way, the app will merrily continue working with corrupt data, which gives it a lot more opportunities to add garbage to your data without noticing, than if it crashed right away before having done too much damage.

Brief Stub Code Reviews

+UINT WINAPI GetRawInputDeviceList(PRAWINPUTDEVICELIST pRawInputDeviceList, PUINT puiNumDevices, UINT cbSize)
+{
+    memset(pRawInputDeviceList, 0, sizeof *pRawInputDeviceList);
+    *puiNumDevices = 0;
+    FIXME("stub\n");
+    return 0;
+}

D. Riekenberg [Oct 06]: The FIXME should be the first code in the stub and is not informative. I suggest to use:

FIXME("(%p, %p, %u) stub\n", pRawInputDeviceList, puiNumDevices, cbSize);

When a different App use NULL for pRawInputDeviceList (documented on MSDN as a vaild Parameter) or puiNumDevices, your stub will still crash, but the FIXME give a usable hint, where to look. wine archive


+BOOL WINAPI FileEncryptionStatusW(LPCWSTR lpFileName, LPDWORD lpStatus)
+{
+    FIXME("%s %p\n", debugstr_w(lpFileName), lpStatus);

Dimi Paun [May 06]: We typically to put "stub" in the FIXME message when we dummy up such functions:

+ FIXME("(%s %p): stub\n", debugstr_w(lpFileName), lpStatus);
+BOOL WINAPI FileEncryptionStatusW(LPCWSTR lpFileName, LPDWORD lpStatus)
+{
+ FIXME("%s %p\n", debugstr_w(lpFileName), lpStatus); + return TRUE;

the developer noted that different conventions had crept in and asked which is right: What is the preferred format? Looking just in the same file i find the following alternatives, all used:

  1. "%s %p\n"
  2. "%s %p - stub\n"
  3. "(%s %p)\n"
  4. "stub (%s %p)\n"
  5. "(%s %p) : stub\n"
  6. "(%s %p):stub\n"
  7. "(%s %p): stub\n"

Dimi Paun referred to the last one as correct. wine archive


H. Leidekker [may 06]: If you need to return TRUE here it would be better to set lpStatus to a meaningful value, for example FILE_SYSTEM_NOT_SUPPORT, otherwise the caller could end up using random data. wine archive

Links

Further Reading

In august 2006 the Developer Hints were moved to the wiki [1]

Personal tools