views:

1007

answers:

5

GetShortPathName() is not working as I expect on XP SP3

http://msdn.microsoft.com/en-us/library/aa364989(VS.85).aspx

Is returning the input string for paths like:

C:\Test\LongFolderNameToTestWith\BinarySearch.ini

exactly as sent?

Yet:

C:\Documents and Settings\LocalService\NTUSER.DAT

Does make short names for the path, so I know I am calling the API correctly.

However:

C:\Documents and Settings\LocalService\BinarySearch.ini

Does not make a short name out of the filename, but does make short names for the path!?

Could someone help me understand this behavior and perhaps suggest a workaround.

Added:

I need to be able to make an 8.3 Path/filename to pass to a legacy app

How can this be done?

Added: SOLUTION

After MUCH reading/experimenting, it seems that the only reliable way to do this is using automation:

' ------------------------------------------------------------
' Library Name:      Microsoft Scripting Runtime 1.0
' Library File:      C:\WINDOWS\system32\scrrun.dll
' ------------------------------------------------------------
' Version Info:
' -------------
' Company Name:      Microsoft Corporation
' File Description:  Microsoft (R) Script Runtime
' File Version:      5.7.0.16599
' Internal Name:     scrrun.dll
' Legal Copyright:   Copyright (C) Microsoft Corp. 1996-2006, All Rights Reserved
' Original Filename: scrrun.dll
' Product Name:      Microsoft (R) Script Runtime
' Product Version:   5.7.0.16599
' ------------------------------------------------------------
' ProgID:            Scripting.FileSystemObject
' Interface Name:    ScriptingFileSystemObject
'
' Interface Prefix:  Scripting

This works.

A simple implementation in BASIC would be:

$PROGID_ScriptingFileSystemObject = "Scripting.FileSystemObject"

Interface Dispatch ScriptingFileSystemObject
    Member CALL GetFile  <&H0000271C>(IN FilePath   AS STRING<&H00000000>) AS ScriptingIFile
    Member CALL GetFolder<&H0000271D>(IN FolderPath AS STRING<&H00000000>) AS ScriptingIFolder 
END Interface

Interface Dispatch ScriptingFile
    Member GET ShortPath<&H000003EA>() AS STRING
    Member GET ShortName<&H000003E9>() AS STRING    
END Interface

Interface Dispatch ScriptingFolder
    Member GET ShortPath<&H000003EA>() AS STRING
    Member GET ShortName<&H000003E9>() AS STRING
END Interface


'-----------------------------------------------------------------------------      
FUNCTION FileShortPath( BYVAL sPathnFile AS STRING, sShort AS STRING ) AS LONG

  LOCAL vResult, vFilePath AS Variant

  LOCAL fso   AS ScriptingFileSystemObject
  LOCAL oFile AS ScriptingFile


    IF LEN(sPathnFile) = 0 THEN EXIT FUNCTION  ' Nothing sent

    SET fso   = NEW ScriptingFileSystemObject IN $PROGID_ScriptingFileSystemObject
    IF IsNothing(fso) THEN FUNCTION = -1 : EXIT FUNCTION

    SET oFile = NEW ScriptingFile             IN $PROGID_ScriptingFileSystemObject
    IF IsNothing(oFile) THEN FUNCTION = -2 : EXIT FUNCTION     


    vFilePath = sPathnFile 

    vResult = Empty
    OBJECT CALL fso.GetFile(vFilePath) TO vResult

    SET oFile = vResult 
    IF IsNothing(oFile) THEN FUNCTION = -3 : EXIT FUNCTION 

    vResult = Empty
    Object GET oFile.ShortName TO vResult
    sShort = VARIANT$(vResult) 

    vResult = Empty
    Object GET oFile.ShortPath TO vResult
    sShort = VARIANT$(vResult) 

    IF LEN(sShort) THEN FUNCTION = 1 ' Success

END FUNCTION

Thank you all for your suggestions.

+8  A: 

It is because the file name does not have an existing short name and XP SP3 is not automatically creating a short name for the file.

You can check this registry setting (if it exists) to see what it is currently set to.

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\FileSystem\NtfsDisable8dot3NameCreation

When NtfsDisable8dot3NameCreation is set to 1 you will get the following behaviour:

If a folder/file already has a short name, for example "Program Files", then it will return the short name for that folder/file. But if a short name doesn't exist, you will instead get the long name for the file as it is the only name that exists for that object. If short names are disabled then there is no short name to get.

Steven
Yes I looked at that, it is set to 1, but I assumed this setting enabled all NTFS filenames to be created as 8.3 on disk and everywhere, (which is clearly undesirable).When you say "creating a short name for the file" do you mean creatying a new 8.3 filename on the HD or just creating one to return in the call to GetShortPathName()?If that flag does control this, then why is it returning 8.3 for some paths when set to 1?
Mike Trader
I have added some more info to my answer. GetShortPathName() won't create a short name when short name creation is disabled, so you only get short names if they already exist.
Steven
Ah ha. Thank you. And so just to be clear about the scope of this setting, I am not going to find every file I create on the hard drive with a shortname from hence forth, correct?
Mike Trader
+2  A: 

According to the documentation cited earlier, short names will be generated only if the NtfsDisable8dot3NameCreation is 0. If the value was changed, you may have some files/directories with only long names. This would explain why your call to GetShortPathName can short names for the directory and long names for the file.

I haven't been able to confirm this but I suspect there may be special logic in Windows that always creates short names for critical directories such as "Documents and Settings" because some ancient programs might break if this were not done.

jdigital
"Documents and Settings" is likely created during installation, before you even have a chance to disable 8.3 name generation. That would require no special magic.
MSalters
A: 

I am still trying to find a way to reliably make an 8.3 Path/filename.

Is there any way to do this apart from using GETSHORTPATHNAME?

Solved. See above

It seems that MS has continued support for this for COM deciples only... why it is now unreliable in the C API remains a mystery.

Mike Trader
1. This should be an edit to the question, not an answer.2. If the file doesn't have a short name, then no API will return what doesn't exist.3. It might help if you could explain what problem you're trying to solve. Are you running into "Path too long" errors? Are you trying to shell out to an app that can't accept spaces in a path?
Joe White
1. If I understood the logic behind the ranking of posts, then I would still be experimenting with adding responses and starting new threads. It might be more helpful to link to a description of this...2. Perhaps then the issues is creating a short name?3. This is a "why is that the question" answer. But to answer it, yes I am shelling to a legacy app that MUST have an 8.3 path or it fails.
Mike Trader
A: 

Short file names of rely on each other - longfilename1.abc and longfilename2.abc will be represented as longfi~1.abc and longfi~2.abc (or similarly). If you only had the second file, it would have had the first one's short representation. Since there's no one to one 8.3 representation per file name, it just can't be calculated correctly without being able to check the short representation of other files in the same directory.

If you're looking for a 8.3 representation for your own reasons, you can built your own mechanism based on the short name algorithm described here and there around the web (for starters). However, you won't be able to use those names in any of the Windows API functions, as Windows just doesn't support that feature given the described registry settings.

Lastly, if you do choose to implement your own short name methods and you would like to have a way to attach a short name to a physical file, you can use Alternate Data Streams. You will not be able to use them as a real file name with Windows API, but you can create a translation layer that will let you use them (though quite inefficiantly...).

eran
<Short file names of rely on each other> yes<If you're looking for a 8.3 representation for your own reasons> no<not be able to use them as a real file name with Windows API> unfortunatly this IS what I need. I thank you for your answers tho.
Mike Trader
The identifiers are "stable" insofar as short-names are physically saved to the FS. This behavior is what causes failings mentioned above (when said short-names are NOT created) and why GetShortPathName (which requires a valid path) doesn't work correctly here.
pst
+2  A: 

Have you tried SetFileShortName?

jdigital
Thank you. Nice idea... http://msdn.microsoft.com/en-us/library/tes8ehwe(VS.85).aspx (COM object using pInvoke perhaps) - assumes that I have ShortName to set. How do I reliably make a shortname in XP?
Mike Trader
http://en.wikipedia.org/wiki/8.3_filename discusses a set of conventions for short file names. Implement this in a loop where you check the return code of SetFileShortName, and if it fails, increment the count at the end and try again.
jdigital