views:

409

answers:

4

Hi,

I'm getting some serious weirdness using FileVersionInfo.GetVersionInfo() and was hoping somebody might be able to help.

The basics of the issue is that I am iterating through all the files in a folder calling GetVersionInfo() on each. There are about 300 files. This works ok for all but 2 of the files. For these DLLs I am getting comepletely incorrect info back from GetVersionInfo().

In order to eliminate all other variables, I extracted this call into a simple test app and it still got the same problem. However, if I built the test app as a Windows Application (it was a Console Application initially) then the data came back correct.

Just to clarify, the incorrect data coming back when running as a Console App is not simply null info like you would get if the file didn't contain version data. It contained reasonable data, but just the wrong data. It's as if it's reading it from a different file. I've looked for a file that contains matching version data, but can't find one.

Why is this simple call functioning differently if built as a Console Application rather than a Windows Application?

If anyone can help with this I would be very grateful.

Rgds, Andy

-- Code Added

using System;
using System.Diagnostics;

namespace test
{
    class Program
    {
        static void Main(string[] args)
        {
            string file = "C:\\ProblemFile.dll";
            FileVersionInfo version = FileVersionInfo.GetVersionInfo(file);
            string fileName = version.FileName;
            string fileVersion = version.FileVersion;

            Console.WriteLine(string.Format("{0} : {1}", fileName, fileVersion));
        }
    }
}
A: 

Sure the "files" you're seeing aren't . and .. ? If you iterate through all files, you'll always see entries for . (current dir) and .. (up dir). GetVersion Info might well return anything for these. You'd have to filter these entries out manually by name.

Bob Moore
Thanks Bob, but yes I'm sure this isn't the case. In the test app that I mentioned I have hard coded the filename and am simply calling GetVersion() once on one of the problem files.
Andy
A: 

File and Assembly versions are 2 different things.

Are you sure you are not expecting the other?

leppie
Not sure about Assembly Version.When I look in the FileVersionInfo I can see FileVersion and ProductVersion. When I build as a Console App both these are "1, 0, 0, 1". When I build as a Windows App both are "4, 2, 004, 6102". If I pull up the properties from Windows Explorer and look in the Version Tab, Other version information then File Version and Product Version both show as "4, 2, 004, 6102".
Andy
There are 2 different attribute you apply in 'AssemblyInfo.cs', perhaps they are different. That's all I can think.
leppie
Ah, I can see that in my project, but it doesn't show up in the properties pane for the problem DLLs. These are not .NET dlls, I think they're VS6.0 C++
Andy
A: 

Update: Tried this. Didn't work.

using System;
using System.Diagnostics;
using System.Runtime.InteropServices;

namespace test
{
    class Program
    {
        [DllImport("COMCTL32")]
        private static extern int InitCommonControls(int nExitCode);

        static void Main(string[] args)
        {
            InitCommonControls(0);

            string file = "C:\\ProblemFile.dll";
            FileVersionInfo version = FileVersionInfo.GetVersionInfo(file);
            string fileName = version.FileName;
            string fileVersion = version.FileVersion;

            Console.WriteLine(string.Format("{0} : {1}", fileName, fileVersion));
        }
    }
}
Andy
+3  A: 

This behaviour seems weird indeed. Could it be that the Console application does not load the DLL from the same place as the WinForms application does? This would mean that GetVersionInfo uses some other API than just Win32 CreateFile (maybe going through some DLL resolver mechanism, side-by-side or whatever); remember that under the covers, version.dll will be executing your request, not the CLR itself.

Looking at FileVersionInfo through Reflector points in another direction yet:

public static unsafe FileVersionInfo GetVersionInfo(string fileName)
{
    // ...
    int fileVersionInfoSize = UnsafeNativeMethods.GetFileVersionInfoSize(fileName, out num);
    FileVersionInfo info = new FileVersionInfo(fileName);
    if (fileVersionInfoSize != 0)
    {
        byte[] buffer = new byte[fileVersionInfoSize];
        fixed (byte* numRef = buffer)
        {
            IntPtr handle = new IntPtr((void*) numRef);
            if (!UnsafeNativeMethods.GetFileVersionInfo(fileName, 0, fileVersionInfoSize, new HandleRef(null, handle)))
            {
                return info;
            }
            int varEntry = GetVarEntry(handle);
            if (!info.GetVersionInfoForCodePage(handle, ConvertTo8DigitHex(varEntry)))
            {
                int[] numArray = new int[] { 0x40904b0, 0x40904e4, 0x4090000 };
                foreach (int num4 in numArray)
                {
                    if ((num4 != varEntry) && info.GetVersionInfoForCodePage(handle, ConvertTo8DigitHex(num4)))
                    {
                        return info;
                    }
                }
            }
        }
    }
    return info;
}

As you can see there, some interesting dance is going on with code pages. What if the DLLs you inspected had several version information resources attached to them? Depending on the culture of the program calling into GetVersionInfo, I guess that the code page related calls could return other results?

Take the time to check the resources of the DLLs and make sure that there is only one language/code page for the version information. It might point you at the solution, I hope.

Pierre
Thanks Pierre, you definately seem to be onto something here. The DLLs in question have two version resources in them, one en-GB and one en-US. The US version has the correct data in. Changing my culture to en-US does indeed make the Console App pick up the en-US version. However the Windows App picks up the en-US version even when my culture is en-GB. Any idea why this might happen?
Andy
Hi Andy. I'd propose you click the upvote button for the answer, if you find it useful. This is how StackOverflow works ;-). And no, alas, I've no idea how cultures are derived. You could try accessing `System.Threading.Thread.CurrentThread.CurrentCulture` and compare results between console and WinForms apps.
Pierre
Have done so now. Didn't have enough rep before! Have looked at CurrentCulture and CurrentUICulture and they are the same (en-GB, en-US respectively) in both Console and Windows. Changing them both to en-US changes the Console App, but nothing seems to change the Windows App. It still leaves questions but it's given me enough to work with these two DLLs. Thanks for your help.
Andy
Glad I was of any help. I did a lot of guess-work ;-)
Pierre