2

I have an old VB6 application that saves its data to an ini file, which it expects to be in the windows folder. In the VB6 code, I just refer to the file name (without path) and it is located automatically by Windows in the Windows folder. On Windows 8 and corporate networks however, windows moves the file to the user account, so it appears under the \Users\username\AppData folder structure.

VB6 couldn't care less, it always reads "ipmdata.ini" and gets the user specific one handed to it by Windows from the AppData folder.

However, I now need to read this same ini file from a C# application. So I added the definitions for GetPrivateProfileString and fed it with the same parameters I give to VB6. The C# code always reads from the file in the windows folder, not the same file that VB6 is reading and writing.

Any ideas how I make the C# call do the same as the VB6 call to the same API?

I have tried targeting the .Net application against Framework 2.0, compiling to x86 and running editbin to set /TSAWARE:NO on the final executable (though I do not know how to check that works - it just didn't 'fail').

The VB6 code consists of a declare and a read:

Public Declare Function GetPrivateProfileString Lib "kernel32" _
     Alias "GetPrivateProfileStringA" ( _
     ByVal lpApplicationName As String, _
     ByVal lpKeyName As Any, _
     ByVal lpDefault As String, _
     ByVal lpReturnedString As String, _
     ByVal nSize As Long, _
     ByVal lpFileName As String) As Long

   nRetcode = GetPrivateProfileString("PCD", "TemplateFolder", "", sTemp, 512, "mystuff.ini")

The equivalent code in the c# application consists of:

[DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
    static extern uint GetPrivateProfileString(
       string lpAppName,
       string lpKeyName,
       string lpDefault,
       StringBuilder lpReturnedString,
       uint nSize,
       string lpFileName);

        StringBuilder sb = new StringBuilder(2048);
        GetPrivateProfileString("PCD", "TemplateFolder", "", sb, 2048, "mystuff.ini");

There is nothing else in the test programs just this code to read the ini file. The C# code reads the ini currently in my \Windows folder and the VB6 code reads from the \users\user\appdata\virtualstore\windows folder.

Erik Philips
  • 53,428
  • 11
  • 128
  • 150
sab
  • 721
  • 6
  • 7
  • 1
    It would help if you show the relevant code for reading your ini files in both VB and C# you currently use. If you just use `Private Declare Function GetPrivateProfileString Lib "kernel32"` from VB and `[DllImport("kernel32.dll"] static extern uint GetPrivateProfileString()` in C#, I doubt that the API call causes that behavior, because they are the same. Perhaps there is other code interfering with the path, or the API call looks in the current directory, which may be different for the different apps. – CodeCaster Jan 26 '14 at 12:11
  • Also, when porting an app that relies on such [ancient WinAPI calls](http://msdn.microsoft.com/en-us/library/windows/desktop/ms724353(v=vs.85).aspx), you might want to consider [using a library](https://code.google.com/p/ini-parser/) to replace them. – CodeCaster Jan 26 '14 at 12:15
  • Since you're porting the application to .NET why not port the .ini too? You can add settings to the app.config file. The config file is stored in the same folder as the app. Here is an MSDN article with a more detailed explanation. http://msdn.microsoft.com/en-us/library/a65txexh.aspx – jac Jan 26 '14 at 12:37
  • Sorry guys, I didn't get any notifications of comments. I'll update the post with the code.However, as a test I stripped this down to the absolute minimum. So there is a test VB6 program that consists of a declare and a C# program with the same limited code. I'm not porting the app. I have a new requirement that I need to do in .Net. The old vB6 app will remain as-is. – sab Jan 26 '14 at 15:11
  • Just for information, I've embedded a manifest in to the application now and explicitly changed the security options to enable virtualisation. I also changed the program to write to the ini file before trying to read it. Sadly, no difference; VB6 reads the virtualised one and .Net reads the real one. I guess I'll write code to go look for the virtualised ini file first and only read the windows one if I cannot find a virtualised one. It's a junk solution but may be the best I have. – sab Jan 27 '14 at 08:51
  • FWIW at least I understand how I got here. The installer for the VB6 application ran as Administrator and created the initial ini file. The user then ran the program under their non-admin user and windows virtualised their ini file when they tried to write it. I guess I just need to hack it since .net won't honour the virtualised windows folder. Thanks for the suggestions. – sab Jan 27 '14 at 19:06

2 Answers2

0

Have you tried specifying the complete path?

GetPrivateProfileString("PCD", "TemplateFolder", "", sb, 2048, Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\mystuff.ini");
jac
  • 9,666
  • 2
  • 34
  • 63
  • Yes, doesn't matter whether I specify the full path or no path. – sab Jan 27 '14 at 07:51
  • @sab I don't understand because I tested that before I posted and it grabbed from the expected folder. – jac Jan 27 '14 at 15:20
  • What you are doing is different. You have specifically gone for the AppData folder. The VB6 code writes to what it thinks is the Windows folder and Windows redirects to the virtualised folder. The same code in C# does not get redirected. What I need to achieve is to have Windows treat my .net code in the same way as VB6 and redirect it to the virtualised windows folder. – sab Jan 27 '14 at 17:07
0

Lots of misconceptions here.

UAC began back in 2006 with Windows Vista, not Windows 8.

Filesystem virtualization is not an application feature, but a bug. Programmers have been explicitly warned by Microsoft not to rely on it.

Your .Net program probably has a manifest marking it as Vista Aware, in which case virtualization never applies.

This also appears to be a duplicate of Force an existing application to always run with UAC virtualization on

Basicaly the real answer is to fix your program so that it doesn't need virtualization.

Community
  • 1
  • 1
Bob77
  • 13,167
  • 1
  • 29
  • 37
  • I agree, it feels like a bug to me. Sadly it's a bug I need to live with. I modified my .net application to 32-bit and added a manifest that should have enabled virtualisation. Didn't work. My feeling is that something else is going on in the background when the program gets compiled that enables virtualisation. I have an old copy of VS2008 on a VM and will try building the application there to see if it makes any difference. Virtualisation isn't something I wanted to use and not something I asked for. It was imposed on me. – sab Jan 27 '14 at 22:37
  • Then just duplicate the behavior of the file virtualization process instead of trying to *use* virtualization. Look for the virtualized INI and if not there, create it there, by copying the one from the Windows folder. – Bob77 Jan 28 '14 at 05:31
  • That comes with its own problems in that the api has no method of finding out what the virtualisation folder is called and I can't hard code the name as it can be localised. For the purposes of this application, I guess I start at whatever folder contains AppData and perform a recursive search until I find the ini file or run out of places to look. I can get away with that for this specific application. Just would have been nice to use a published interface. – sab Jan 28 '14 at 07:40