Q: current directory in Windows

Discussion of chess software programming and technical issues.

Moderators: hgm, Rebel, chrisw

User avatar
hgm
Posts: 27796
Joined: Fri Mar 10, 2006 10:06 am
Location: Amsterdam
Full name: H G Muller

Q: current directory in Windows

Post by hgm »

I ran into an annoying problem in WinBoard that I don't understand. WinBoard is supposed to interpret all filenames (e.g. of engine directories, GUI book, position file) relative to the folder it is installed in. I therefore configure WinBoard in the installer such that all filenames specified in the various ini files are relative path names, so that the user can install it in whatever folder he wants.

To make this work, WinBoard uses a variable 'installDir' to translate any relative filenames to absolute paths. The installDir path is obtained at startup by calling the MicroSoft function SearchPath with a NULL argument for the path.

The problem is that this does not always work! Especially now that I am creating an installer including the 'portable' version of WinBoard, where you start WinBoard in the various modes by dragging files on top of its icon. I then often get complaints that INI files containing the settings for the mode WinBoard needs to start in for processing that file type (e.g. a PGN or a tourney file) can not be opened. Sometimes it works, but specifically when I used that WinBoard to point at a file in another folded in the browser dialog (e.g. for installing another board texture), the drag & drop startup always fails. I figured out that this is because it is then looking for them in the folder from which you last pointed out a file by browsing: the winboard.debug file is created there.

This situation continues to exist even when I close WinBoard and restart it. But when I let WinBoard print the value of 'installDir' in its debug file, it does print the path name of the folder where it is installed. So it seems I can cure the problem by doing a SetCurrentDirectory to the installDir that is retrieved by SearchPath.

Code: Select all

  if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {
    *filepart = NULLCHAR;
    SetCurrentDirectory(installDir); // [HGM] newly added
  } else {
    GetCurrentDirectory(MSG_SIZ, installDir);
  }
The problem is that I don't understand why this is needed. If the installDir isn't already the current directory, what criterion does Windows use to decide what to return for it with SearchPath? I have many files called winboard.exe on my computer, in many different folders. How can it know which one to return if the current directory is in a completely unrelated place. Are their multiple 'current directories' on Windows? Is SearchPath a safe function to use for this?

I noticed this on Windows 7.
rbarreira
Posts: 900
Joined: Tue Apr 27, 2010 3:48 pm

Re: Q: current directory in Windows

Post by rbarreira »

http://msdn.microsoft.com/en-us/library ... 85%29.aspx
If the lpPath parameter is NULL, SearchPath searches for a matching file based on the current value of the following registry value:

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\SafeProcessSearchMode

When the value of this REG_DWORD registry value is set to 1, SearchPath first searches the folders that are specified in the system path, and then searches the current working folder. When the value of this registry value is set to 0, the computer first searches the current working folder, and then searches the folders that are specified in the system path. The system default value for this registry key is 0.

The search mode used by the SearchPath function can also be set per-process by calling the SetSearchPathMode function.
Have you checked that value in the registry? It seems that using the SetSearchPathMode function may solve your problem.
User avatar
hgm
Posts: 27796
Joined: Fri Mar 10, 2006 10:06 am
Location: Amsterdam
Full name: H G Muller

Re: Q: current directory in Windows

Post by hgm »

I don't know how I can check that. (My Windows knowledge is very limited.) Also note that SearchPath does return the correct path (the path of the folder where winboard.exe actually is), but that the problem was that this is not the current directory.

In the mean time I also stumblen to another problem. The installer associates file type .xop with WinBoard, to use it as settings file (similar to .ini, but .ini is in general use and could ot be associated), and start WB with the options in there. This did not seem to work; double-clicking a .xop file did start WinBoard, but it could not find engines or other files specified in the .xop. While dragging the same .xop file onto the winboard.exe icon worked without problems.

I now discovered that this is because the .xop files have remained associated with an older WinBoard version, that indeed does not have those engines or files in its folders. It seems impossible, however, to associate the .xop filetype with the new winboard.exe. I removed the old one, (by renaming), and redid the install, but then double-clicking the .xop file just brigs up the 'Open with... -> Other' dialog. WinBoard is not amongst the choies there, and if I select Browse... to point at the desired winboard.exe, it still does ot appear amongst the 'Other' programs. In fact, nothing appears there. When I browse to the renamed old winboard.exe, it does appear in the 'Other' pane of the dialog, together with a host of other programs.

So it seems that something is blocking the possibility to associate the newly installed winboard.exe with this file type (or in fact any file type). This is no doubt why the installer did not manage to do it either during the install.

What could cause this pathological behavior? (And how could it be cured?)
tvrzsky
Posts: 128
Joined: Sat Sep 23, 2006 7:10 pm
Location: Prague

Re: Q: current directory in Windows

Post by tvrzsky »

hgm wrote:I don't know how I can check that. (My Windows knowledge is very limited.)
Use Registry Editor: Start=>Run=>regedit or just type regedit in the command line.
Sven
Posts: 4052
Joined: Thu May 15, 2008 9:57 pm
Location: Berlin, Germany
Full name: Sven Schüle

Re: Q: current directory in Windows

Post by Sven »

tvrzsky wrote:
hgm wrote:I don't know how I can check that. (My Windows knowledge is very limited.)
Use Registry Editor: Start=>Run=>regedit or just type regedit in the command line.
In the "cmd" window:

Code: Select all

reg query "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\SafeProcessSearchMode"

and to add that value if not present:

reg add "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager" /v SafeProcessSearchMode /t REG_DWORD /d 1
or
reg add "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager" /v SafeProcessSearchMode /t REG_DWORD /d 0

and to delete it:

reg delete "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager" /v SafeProcessSearchMode
and of course you are always careful when modifying the registry :-)

Sven
Antonio Torrecillas
Posts: 90
Joined: Sun Nov 02, 2008 4:43 pm
Location: Barcelona

Re: Q: current directory in Windows

Post by Antonio Torrecillas »

As stated in:
http://msdn.microsoft.com/en-us/library ... s.85).aspx

in the createprocess you need to identify the complete path to the executable, or do this relative to the current directory.
That said, your argv[0], when your program is started from explorer, must be the full path of the executable.
The current directory is inherited from the parent process explorer.exe.
In the process of drag and drop of a file into a program's icon. argv[0] is the full path of the executable (as usual :-) )
and argv[1] is the fullpath of the dragged file. The current directory is the drag directory, so the directory from which you start the action.

A _splitpath(argv[0],... followed with a makepath function must be enought to construct your installdir, without playing with registry or search orders(path etc).

You can verify this with a test program like:

Code: Select all

#include <Windows.h>

int main&#40;int argc, char* argv&#91;&#93;)
&#123;
	char buffer&#91;512&#93;;
	GetCurrentDirectory&#40;512,buffer&#41;;
	printf&#40;"argc %d argv&#91;0&#93; = %s argv&#91;1&#93; = %s\n",argc,argv&#91;0&#93;,argv&#91;1&#93;);
	printf&#40;"CurrentDir %s\n",buffer&#41;;
	getchar&#40;);
	return 0;
&#125;
note that:
The SearchPath function is not recommended as a method of locating a .dll file if the intended use of the output is in a call to the LoadLibrary function. This can result in locating the wrong .dll file because the search order of the SearchPath function differs from the search order used by the LoadLibrary function. If you need to locate and load a .dll file, use the LoadLibrary function.

There is yet another case when you drop a file in an already open program, this one is handled via clipboard, but this start to be a bit off-topic.
User avatar
hgm
Posts: 27796
Joined: Fri Mar 10, 2006 10:06 am
Location: Amsterdam
Full name: H G Muller

Re: Q: current directory in Windows

Post by hgm »

Antonio Torrecillas wrote:The current directory is inherited from the parent process explorer.exe.
In the process of drag and drop of a file into a program's icon. argv[0] is the full path of the executable (as usual :-) )
and argv[1] is the fullpath of the dragged file. The current directory is the drag directory, so the directory from which you start the action.
OK, this inded is confirmed. Because WinBoard is a GUI app I seem to have no direct access to argc, argv, and the iitial entry point looks like:

Code: Select all

int APIENTRY
WinMain&#40;HINSTANCE hInstance, HINSTANCE hPrevInstance,
	LPSTR lpCmdLine, int nCmdShow&#41;
One of the first things it does is call InitInstance, in which I put the code:

Code: Select all

BOOL
InitInstance&#40;HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine&#41;
&#123;
  HWND hwnd; /* Main window handle. */
  int ibs;
  WINDOWPLACEMENT wp;
  char *filepart, curDir&#91;MSG_SIZ&#93;;

  hInst = hInstance;	/* Store instance handle in our global variable */
  programName = szAppName;

  if &#40;SearchPath&#40;NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart&#41;) &#123;
    *filepart = NULLCHAR;
    GetCurrentDirectory&#40;MSG_SIZ, curDir&#41;;
    SetCurrentDirectory&#40;installDir&#41;; // just added
  &#125; else &#123;
    GetCurrentDirectory&#40;MSG_SIZ, installDir&#41;;
  &#125;
  gameInfo.boardWidth = gameInfo.boardHeight = 8; // &#91;HGM&#93; won't have open window otherwise
  screenWidth = screenHeight = 1000; // &#91;HGM&#93; placement&#58; kludge to allow calling EnsureOnScreen from InitAppData
  InitAppData&#40;lpCmdLine&#41;;      /* Get run-time parameters */
  /* xboard, and older WinBoards, controlled the move sound with the
     appData.ringBellAfterMoves option.  In the current WinBoard, we
     always turn the option on &#40;so that the backend will call us&#41;,
     then let the user turn the sound off by setting it to silence if
     desired.  To accommodate old winboard.ini files saved by old
     versions of WinBoard, we also turn off the sound if the option
     was initially set to false. &#91;HGM&#93; taken out of InitAppData */
  if (!appData.ringBellAfterMoves&#41; &#123;
    sounds&#91;&#40;int&#41;SoundMove&#93;.name = strdup&#40;"");
    appData.ringBellAfterMoves = TRUE;
  &#125;
  if &#40;appData.debugMode&#41; &#123;
    debugFP = fopen&#40;appData.nameOfDebugFile, "w");
    setbuf&#40;debugFP, NULL&#41;;
    fprintf&#40;debugFP, "Install&#58; %s Initial&#58; %s Command %s\n", installDir, curDir, lpCmdLine&#41;;
  &#125;
Thea last line causes printing of:

Install: C:\WinBoard-4.7.0\WinBoard\ Initial: C:\cygwin\home\shogi Command C:\cygwin\home\shogi\Pulsar.xop

It seems that lpCmdLine only contains argv[1] and later, not argv[0]. But the initial directory is indeed the one I clicked the Pulsar.xop in, and it is different from the WinBoard folder. So the SetCurrentDirectory that I just added is essential to end up in the WinBoard folder.

That does still beg the question how SearchPath did know it should have returned C:\WinBoard-4.7.0\WinBoard\winboard.exe, and not, for instance, C:\WinBoard-Chu\winboard.exe. It certainly could not have seen that from the current directory. Some other hidden information must have been passed from explorer.exe to the WinBoard process...

Strange thing is that I now cannot reproduce the problem I had after browsing, which should have led to printing of another initial path name.

Another piece of info: when I make a copy of winboard.exe and rename it blupko.exe, I can make it appear in the 'Open With' dialog, and associate files to it. And when it appears in that dialog, it does appear as 'WinBoard 32-bit GUI for Chess. there, so it was not fooled by the name change. But there seems to be a ban on associating files with something named winboard.exe...
Antonio Torrecillas
Posts: 90
Joined: Sun Nov 02, 2008 4:43 pm
Location: Barcelona

Re: Q: current directory in Windows

Post by Antonio Torrecillas »

OK, for the argv[0] use

Code: Select all

	TCHAR buffer&#91;512&#93;;
	GetModuleFileName&#40;NULL,buffer,512&#41;;