Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

GetPrivateProfileSection results are truncated #172

Closed
ryannewington opened this issue Oct 16, 2020 · 14 comments
Closed

GetPrivateProfileSection results are truncated #172

ryannewington opened this issue Oct 16, 2020 · 14 comments

Comments

@ryannewington
Copy link

Describe the bug
GetPrivateProfileSection is used to return a section from an INI file.

Consider the following example

[Unicode]
Unicode=yes
[Version]
signature="$CHICAGO$"
Revision=1

I can call the GetPrivateProfileSection function to return the contents of say the Version section. The API returns the results in the following format

signature="$CHICAGO$"\0Revision=1\0\0

When the StringBuilder used in the PInvoke declaration encounters the first null character, it stops building the string, and the rest of the result is discarded.

What code is involved

string inf = "C:\temp\my.ini"
StringBuilder sectionData = new StringBuilder(32000);

var bytesFilled = Kernel32.GetPrivateProfileSection("Version", sectionData, 32000, inf);

// sectionData will now hold only the first element

Expected behavior
All items should be available. Perhaps an array of strings is more appopriate.

@dahall
Copy link
Owner

dahall commented Oct 16, 2020

The values are there, but you have to know a little trick to extract them: sectionData.ToString().Split('\0', StringSplitOptions.RemoveEmptyEntries) will return the list of strings. However, this may not be obvious to all developers so I'll add an overload that takes a byte array.

@ryannewington
Copy link
Author

I gave that a go, but I still only get the first result. There are 4 entries in this section.

image

dahall added a commit that referenced this issue Oct 17, 2020
@dahall
Copy link
Owner

dahall commented Oct 17, 2020

I just added overloads to those methods that take an IntPtr instead of StringBuilder. This should allow you to allocate memory and pass it to the methods. You can then do the following:

string inf = @"C:\temp\my.ini";
var chars = 32,767;
using var mem = new SafeCoTaskMemHandle(chars * StringHelper.GetCharSize());
var bytesFilled = Kernel32.GetPrivateProfileSection("Version", mem, chars, inf);
return mem.ToStringEnum();

If you'd like to try this before the next release, you can check the project home page for info on the AppVeyor prerelease NuGet feed.

@ryannewington
Copy link
Author

Brilliant. That is working great, thanks David.

Was I doing something wrong with the StringBuilder?

@dahall
Copy link
Owner

dahall commented Oct 17, 2020

No. It appears that different versions of .NET handle StringBuilder internals differently. I'm actually working on some better wrappers so you can simply call : return Kernel32.GetPrivateProfileSection("Version", inf) and get a string[] with the values. I'll let you know when those are done.

@dahall
Copy link
Owner

dahall commented Oct 18, 2020

Would a wrapper class be a valuable addition to do something like the following?

var ini = new InitializationFile(@"C:\temp\my.ini");
var versionSettings = ini.Sections["Version"];
foreach (var keyValuePair in versionSettings)
   Console.WriteLine($"{keyValuePair.Key}={keyValuePair.Value}");
versionSettings["MyNewKey"] = "some value";

@dahall
Copy link
Owner

dahall commented Oct 18, 2020

I just added one to the AppVeyor build. Look for Vanara.Configuration.InitializationFile. I also just pushed a bunch of changes to make the native API functions behave. I removed many of the StringBuilder based functions. Please give me your thoughts.

@ryannewington
Copy link
Author

Yeah thats an awesome idea. I turned to the API because most other parsers are regex or similarly based and I didn't feel comfortable with that. I'll convert my code over to use this and let you know how it goes

@ryannewington
Copy link
Author

Is it in the Vanara.PInvoke.Kernel32 package? I'm not seeing an updated version on AppVeyor.

@dahall
Copy link
Owner

dahall commented Oct 18, 2020

Yes. I got confirmation of the build a few minutes ago.

@dahall
Copy link
Owner

dahall commented Oct 18, 2020

Sometimes VS has a hard time updating packages whose version didn't change. You may need to flush your NuGet caches.

@dahall
Copy link
Owner

dahall commented Oct 21, 2020

Released!

@dahall dahall closed this as completed Oct 21, 2020
@ryannewington
Copy link
Author

Sorry - late on the feedback, but I have used it and it is working well. Great idea. Thanks again David.

@dahall
Copy link
Owner

dahall commented Oct 22, 2020

Glad it is useful. I laid in bed stewing on the idea and it came together quickly the next morning.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants