C# と VB.NET の質問掲示板

ASP.NET、C++/CLI、Java 何でもどうぞ

C# と VB.NET の入門サイト

GetPrivateProfileSectionについて

[トピック内 14 記事 (1 - 14 表示)]  << 0 >>

■89040 / inTopicNo.1)  GetPrivateProfileSectionについて
  
□投稿者/ UMS (1回)-(2018/10/28(Sun) 16:27:51)

分類:[VB.NET/VB2005 以降] 

2018/10/28(Sun) 16:35:50 編集(投稿者)
2018/10/28(Sun) 16:35:47 編集(投稿者)

お世話になっております。UMSと申します。

この度、INIファイルを扱わなければならない事情がありINIファイルから設定値を取得したいと考えております。

その際、INIファイルのセクション毎に設定値を取得すべくフォームロードイベント内でGetPrivateProfileSection関数を用いようと試みているのですが
何故かGetPrivateProfileSection関数の中でフォームが再度立ち上がってしまいます。


以下、ソースです。

API宣言

Private Declare Function GetPrivateProfileSection Lib "kernel32" Alias _
"GetPrivateProfileSectionA" (ByVal lpAppName As String, ByVal lpReturnedString As String,
ByVal nSize As Long, ByVal lpFileName As String) As Integer


Private Function GETINI(Section) As String

Dim FileName As String = ファイルパス
Dim Buffer As String
Dim BufferCap As Integer = 30000(2048もあれば足りるはずですがキャパが不足で上手くいかないのかと思い増やしました。)
Dim Rtn As String = ""

GetPrivateProfileSection(Section(セクション名の引数), Buffer, BufferCap, FileName)←ここを通過した直後でフォームが起動します。

Rtn = Replace(strBuffer.ToString, "\", vbCrLf)(INIファイルは\で区切っているので\ごとに改行しようとしています。)
Return Rtn

キャパ不足ということはまずないと思うのですが何故でしょうか。
何卒よろしくお願いいたします。
引用返信 編集キー/
■89041 / inTopicNo.2)  Re[1]: GetPrivateProfileSectionについて
□投稿者/ Azulean (1001回)-(2018/10/28(Sun) 18:07:04)
Windows Forms において、Load イベントで発生した例外は無視されることがあります。
今回、そのメソッドの実行で例外が発生するが無視され、後の処理はスキップされ、フォームが表示されているのではないでしょうか。

GetPrivateProfileSection の引数を見ていると、As Long となっている箇所がありますが、これは間違っていませんか。
引用返信 編集キー/
■89042 / inTopicNo.3)  Re[2]: GetPrivateProfileSectionについて
□投稿者/ UMS (2回)-(2018/10/28(Sun) 20:05:27)
No89041 (Azulean さん) に返信
> Windows Forms において、Load イベントで発生した例外は無視されることがあります。
> 今回、そのメソッドの実行で例外が発生するが無視され、後の処理はスキップされ、フォームが表示されているのではないでしょうか。
>
> GetPrivateProfileSection の引数を見ていると、As Long となっている箇所がありますが、これは間違っていませんか。


Azuleanさん、大変ご丁寧かつ迅速なお返事を有難うございます。
ご指摘どおり型を間違えてしまっていました。申し訳ありません、こちらの確認不足です。以後、気をつけます。

大変恐縮ですが、ご指摘頂いた点を修正後改めて浮かんだ疑問なのですが

例えば取得した設定値をテキストボックスに表示したり設定値を活用してフォントサイズを変えたりしたい場合、GetPrivateProfileString関数なら設定値の数だけGetPrivateProfileString関数を呼び出してINIファイルから設定値を取得しなければならない
と感じ、調べた結果一度に取得できそうなGetPrivateProfileSection関数を用いようとしていますが、この関数の返り値は設定値そのものではなくINIファイルの中身が

[SECTION]
MOJIRETU = TEST

だとすると MOJIRETU = TEST と そのまま返してしまうようです。 設定値を先述の用途で扱う場合、そもそもGetPrivateProfileSection関数を使用するのは誤りなのでしょうか?
引用返信 編集キー/
■89043 / inTopicNo.4)  Re[3]: GetPrivateProfileSectionについて
□投稿者/ Azulean (1002回)-(2018/10/28(Sun) 20:29:26)
2018/10/28(Sun) 20:29:59 編集(投稿者)

No89042 (UMS さん) に返信
> 例えば取得した設定値をテキストボックスに表示したり設定値を活用してフォントサイズを変えたりしたい場合、GetPrivateProfileString関数なら設定値の数だけGetPrivateProfileString関数を呼び出してINIファイルから設定値を取得しなければならない
> と感じ、調べた結果一度に取得できそうなGetPrivateProfileSection関数を用いようとしています

何を気にするか次第ではないでしょうか。

1.ファイルの読み出しを一度で実行した方が処理時間が短くなる(GetPrivateProfileSection を使う)
2.ファイルの読み出し時間は実感できるほどの差がないので、作りやすいことを意識する(GetPrivateProfileString/GetPrivateProfileInt を使う)


1を気にして GetPrivateProfileSection を選ばれているのだと思いますが、その文字列を分解・解析するコードを自分で書く必要があります。
ユーザーが意図せぬ文字列を入れていた場合にどうするかとか、悩む部分があるかもしれません。

2で1つずつ値を読み出す方は作りやすさという点で利するでしょう。
よっぽどでなければ、個別に読み出しても実感できるほどの遅さはないと思うので、総合的には「2の方が楽かつ、実行時間は大差ない」で選ばれることは多いと思います。
引用返信 編集キー/
■89044 / inTopicNo.5)  Re[4]: GetPrivateProfileSectionについて
□投稿者/ UMS (3回)-(2018/10/28(Sun) 22:49:32)
No89043 (Azulean さん) に返信
> 2018/10/28(Sun) 20:29:59 編集(投稿者)
>
> ■No89042 (UMS さん) に返信
>>例えば取得した設定値をテキストボックスに表示したり設定値を活用してフォントサイズを変えたりしたい場合、GetPrivateProfileString関数なら設定値の数だけGetPrivateProfileString関数を呼び出してINIファイルから設定値を取得しなければならない
>>と感じ、調べた結果一度に取得できそうなGetPrivateProfileSection関数を用いようとしています
>
> 何を気にするか次第ではないでしょうか。
>
> 1.ファイルの読み出しを一度で実行した方が処理時間が短くなる(GetPrivateProfileSection を使う)
> 2.ファイルの読み出し時間は実感できるほどの差がないので、作りやすいことを意識する(GetPrivateProfileString/GetPrivateProfileInt を使う)
>
>
> 1を気にして GetPrivateProfileSection を選ばれているのだと思いますが、その文字列を分解・解析するコードを自分で書く必要があります。
> ユーザーが意図せぬ文字列を入れていた場合にどうするかとか、悩む部分があるかもしれません。
>
> 2で1つずつ値を読み出す方は作りやすさという点で利するでしょう。
> よっぽどでなければ、個別に読み出しても実感できるほどの遅さはないと思うので、総合的には「2の方が楽かつ、実行時間は大差ない」で選ばれることは多いと思います。


お返事有難うございます。
当初、1を選んでいた理由としては2を選択すると、INIファイル内のキーを複数取得したい場合に何度も同じ関数を呼び出さなければならないと思ったからです。
GetPrivateProfileStringであれば第一引数、第二引数をNULLにすれば全セクション、キーを取得できるとは調べて分かったのですが取得したキーをそれぞれ別の変数に格納することは可能でしょうか?
色々考えてみたのですが中々方法が思いつきません。もしよろしければご教示下さらないでしょうか。

引用返信 編集キー/
■89047 / inTopicNo.6)  Re[5]: GetPrivateProfileSectionについて
□投稿者/ Azulean (1004回)-(2018/10/29(Mon) 06:47:30)
2018/10/29(Mon) 06:49:07 編集(投稿者)

No89044 (UMS さん) に返信
> 当初、1を選んでいた理由としては2を選択すると、INIファイル内のキーを複数取得したい場合に何度も同じ関数を呼び出さなければならないと思ったからです。
> GetPrivateProfileStringであれば第一引数、第二引数をNULLにすれば全セクション、キーを取得できるとは調べて分かったのですが取得したキーをそれぞれ別の変数に格納することは可能でしょうか?
> 色々考えてみたのですが中々方法が思いつきません。もしよろしければご教示下さらないでしょうか。

どんな出力だったか覚えてない&最近 ini を使わないので本筋の回答はすぐに思いつく段階にないです。


ところで、出力を切り出す処理を書くことと、GetPrivateProfileString を何行か書くことは行数としてほとんど変わらないと思います。
特に「別々の変数に入れたい」なら、変数の数だけコードの行数は増えるので、GetPrivateProfileString を並べて書いた方が理解しやすくて良いのでは?と思っていたのです。
(Dictionary に代入だけで良いなら、行数は減るかもしれませんが)
引用返信 編集キー/
■89049 / inTopicNo.7)  Re[5]: GetPrivateProfileSectionについて
□投稿者/ 774RR (637回)-(2018/10/29(Mon) 09:07:28)
オイラの記憶っつか実験結果と元発言者さんの理解とが異なるので確認なんだけど

d:\vsproj\initest\initest\initest.ini
[FOO]
FVALUE1 = FOO1
FVALUE2 = FOO2
FVALUE3 = FOO3

[BAR]
BVALUE1 = BAR1
BVALUE2 = BAR2
BVALUE3 = BAR3

であるとき GetPrivateProfileString を、
・第一引数 null としたとき(第二、第三引数に関係なく)得られるのは FOO BAR
・第一引数 "BAR" 第二引数 null としたとき、得られるのは BVALUE1 BVALUE2 BVALUE3
なので、全セクションと全キーを同時に得ることはできないっす。

で、何が得たいのか微妙に理解しがたいんだけど
String[] Sections = { "FOO", "BAR" };
String[] KeyforBAR = { "BVALUE1", "BVALUE2", "BVALUE3" };
なんだったら StringBuilder を \0 で分割すれば得られるはず(LINQ とかでできるのだろうか?よく知らない)


引用返信 編集キー/
■89053 / inTopicNo.8)  Re[3]: GetPrivateProfileSectionについて
□投稿者/ 魔界の仮面弁士 (1898回)-(2018/10/29(Mon) 10:44:59)
No89042 (UMS さん) に返信
>> GetPrivateProfileSection の引数を見ていると、As Long となっている箇所がありますが、これは間違っていませんか。
> Azuleanさん、大変ご丁寧かつ迅速なお返事を有難うございます。
> ご指摘どおり型を間違えてしまっていました。

修正案。

'Private Declare Auto Function GetPrivateProfileSection Lib "kernel32" _
'  (<[In]> ByVal lpAppName As String,
'  <MarshalAs(UnmanagedType.LPTStr, SizeParamIndex:=2)> ByVal lpReturnedString As String,
'  ByVal nSize As Integer,
'  <[In]> ByVal lpFileName As String) As Integer

Private Declare Unicode Function GetPrivateProfileSection Lib "kernel32" Alias "GetPrivateProfileSectionW" _
  (<[In]> ByVal lpAppName As String,
  <MarshalAs(UnmanagedType.LPTStr, SizeParamIndex:=2)> ByVal lpReturnedString As String,
  ByVal nSize As Integer,
  <[In]> ByVal lpFileName As String) As Integer


・既に指摘のある通り、nSize の型は Long ではありません。元が DWORD なので、32bit 整数型が求められます。
・DWORD である以上、本来は UInteger なのですが、ここではあえて Integer としています。
・Win9x が淘汰された現時点で ANSI バージョンを使うことにはメリットが無いので、Unicode 版で宣言しています。
・LPCTSTR な引数には <In> 属性を付与しています。属性指定なしでも一応呼び出せるのですが、
 それだと VB の仕様上、呼び出し元変数の文字列参照が書き換えられてしまうため、それを回避するための処置です。


> Dim BufferCap As Integer = 30000(2048もあれば足りるはずですがキャパが不足で上手くいかないのかと思い増やしました。)
API の戻り値を確認することをお奨めします。
この API はキャパ不足の場合、戻り値として nSize - 2 を返す仕様です。

また、キャパが十分であれば受け取った文字数が返されますので、
その値をバッファーの切り出しに使えます。(Unicode 版の場合)


> GetPrivateProfileString関数なら設定値の数だけGetPrivateProfileString関数を呼び出して
> INIファイルから設定値を取得しなければならない
自分の場合、読み取り時には GetPrivateProfileString しか使わないです。

GetPrivateProfileSection だと、UMS さんが書かれているように再解析が必要ですし、
そもそも指定したセクション内のキーと値しか取得できないので、あまり意味が無い。

GetPrivateProfileSectionNames は、GetPrivateProfileString に Nothing 指定すれば済む話なので。
(GetPrivateProfileStruct は使ったことがありません)


といっても、INI 前提のアプリは長らく作っていないのですけれどね。
.NET からだと、設定ファイルは XML か JSON を利用することが多いので。
(INI だと、レジストリマッピングが使えるという特性があるので、完全な代替にはなりませんが)
引用返信 編集キー/
■89064 / inTopicNo.9)  Re[6]: GetPrivateProfileSectionについて
□投稿者/ UMS (5回)-(2018/10/29(Mon) 20:19:01)
No89047 (Azulean さん) に返信
> 2018/10/29(Mon) 06:49:07 編集(投稿者)
> ところで、出力を切り出す処理を書くことと、GetPrivateProfileString を何行か書くことは行数としてほとんど変わらないと思います。
> 特に「別々の変数に入れたい」なら、変数の数だけコードの行数は増えるので、GetPrivateProfileString を並べて書いた方が理解しやすくて良いのでは?と思っていたのです。
> (Dictionary に代入だけで良いなら、行数は減るかもしれませんが)

 お忙しい中お返事有難うございます。
Azuleanさんの仰るとおり、GetPrivateProfileString関数であるならば視覚的にも何をしているか判断しやすいですし、設定値を取得し変数に格納していきたいという目的ですので代入式も分かりよくなるかもしれません。
ご指摘くださり有難うございます、視野が狭まっておりました。第一引数でセクションを指定し、第二引数でキー値をNULLにすることでセクションに紐づいたキー値を取得できるそうなので何とか変数に格納する方法を模索したいと思います。
引用返信 編集キー/
■89065 / inTopicNo.10)  Re[6]: GetPrivateProfileSectionについて
□投稿者/ UMS (6回)-(2018/10/29(Mon) 20:29:39)
No89049 (774RR さん) に返信
> オイラの記憶っつか実験結果と元発言者さんの理解とが異なるので確認なんだけど
>
> d:\vsproj\initest\initest\initest.ini
> [FOO]
> FVALUE1 = FOO1
> FVALUE2 = FOO2
> FVALUE3 = FOO3
>
> [BAR]
> BVALUE1 = BAR1
> BVALUE2 = BAR2
> BVALUE3 = BAR3
>
> であるとき GetPrivateProfileString を、
> ・第一引数 null としたとき(第二、第三引数に関係なく)得られるのは FOO BAR
> ・第一引数 "BAR" 第二引数 null としたとき、得られるのは BVALUE1 BVALUE2 BVALUE3
> なので、全セクションと全キーを同時に得ることはできないっす。
>
>
774RR さん、お忙しい中ご返信有難うございます。
774RRさんにご指摘いただき、再度msdn(https://msdn.microsoft.com/ja-jp/library/cc429779.aspx)にて関数の確認を行ってきました。774RRさんの挙げてくださった例を拝借して試してみたのですが


GetPrivateProfileString("FOO", vbNullString, "", strBuffer, strBuffer.Capacity, FileName)

とした際に、F001、F002、F003を取得できると考えていたのですが実際のstrBufferに取得されていた値は[FOO]のみでした。
msdnや774RRさんの書いて下さった内容を見比べて理解の浅い点を埋めようとしてみましたが中々何故こうなってしまうのかが分かりません。第一引数で指定したセクション、第二引数でNULL指定することで第一引数のセクションに紐づくキー値を取得する
といった解釈は誤りでしょうか?

> で、何が得たいのか微妙に理解しがたいんだけど

得たい値はセクション毎にキー値として持っている設定値でしてその設定値を活用していろいろな関数に使用したいと考えております。

引用返信 編集キー/
■89067 / inTopicNo.11)  Re[4]: GetPrivateProfileSectionについて
□投稿者/ UMS (7回)-(2018/10/29(Mon) 20:45:19)
No89053 (魔界の仮面弁士 さん) に返信

  お忙しい中お返事有難うございます。
サンプルまで作成してくださり本当に有難うございます。ご指摘の通り、API の戻り値を確認することを怠っておりました。
頂いたサンプルやご説明を基にmsdnなどと照らし合わせGetPrivateProfileSectionの理解を深めることができたと思います。
特に<In>属性の箇所など全く考慮しておらず、大変勉強になりました。有難うございます。


>>GetPrivateProfileString関数なら設定値の数だけGetPrivateProfileString関数を呼び出して
>>INIファイルから設定値を取得しなければならない
> 自分の場合、読み取り時には GetPrivateProfileString しか使わないです。
>
> GetPrivateProfileSection だと、UMS さんが書かれているように再解析が必要ですし、
> そもそも指定したセクション内のキーと値しか取得できないので、あまり意味が無い。
>
> GetPrivateProfileSectionNames は、GetPrivateProfileString に Nothing 指定すれば済む話なので。
> (GetPrivateProfileStruct は使ったことがありません)


GetPrivateProfileString関数を用いることでセクションを指定し、キー値にNothing指定をすればセクションに紐づくキー値を全て取得できるそうなのでそれでやってみたいと思います。
GetPrivateProfileString(指定セクション, vbNullString, "", strBuffer, strBuffer.Capacity, FileName)
とした場合strBufferには指定セクション内のキー値が返ってくると思いますが、指定セクション名が strBufferに返って来ており結果が分からないのですが、キー値を取得した際はキー値が連結されて戻ってくるのでしょうか?
引用返信 編集キー/
■89068 / inTopicNo.12)  Re[7]: GetPrivateProfileSectionについて
□投稿者/ 774RR (638回)-(2018/10/30(Tue) 05:57:05)
Win32 API つまり C/C++ から GetPrivateProfileString を呼ぶと MSDN の解説通り \0 で区切られた結果
FOO\0BAR\0\0 が得られるのを確認済みっす。
一方で VB つか .NET の StringBuilder は仕様上、最初の \0 文字で処理を打ち切るっぽいです。
なので FOO が得られるのも .NET 側の仕様として正しいってことらしい。

だから結果を受け取るバッファは StringBuilder でなくて BYTE の列にしないといけないってことらしい。
BYTE の列を受け取って (Encoding を考慮しながら) String に再変換が必要です。
# オイラ VB は書けないので後は誰かに任せた・・・

引用返信 編集キー/
■89072 / inTopicNo.13)  Re[5]: GetPrivateProfileSectionについて
□投稿者/ 魔界の仮面弁士 (1901回)-(2018/10/30(Tue) 11:11:54)
No89067 (UMS さん) に返信
> 頂いたサンプルやご説明を基にmsdnなどと照らし合わせGetPrivateProfileSectionの理解を深めることができたと思います。
> 特に<In>属性の箇所など全く考慮しておらず、大変勉強になりました。有難うございます。

参考までに:

KEN's .NET - [特集6] GetPrivateProfileStringにみるAPIの使用方法
http://www5b.biglobe.ne.jp/~yone-ken/VBNET/special/sp06_GetPrivateProfileString.html
http://www5b.biglobe.ne.jp/~yone-ken/VBNET/Reference/ref2_GetPrivateProfileString.html


> GetPrivateProfileString(指定セクション, vbNullString, "", strBuffer, strBuffer.Capacity, FileName)

774RR さんも書かれていますが、今回のケースでは StringBuilder は使えません。


> キー値にNothing指定をすればセクションに紐づくキー値を全て取得できるそうなので
セクションとキーを指定すれば、値が得られます。
バッファが足りない場合の戻り値は nSize - 1 です。

セクションを指定して、キーを Nothing にすれば、キーの一覧が vbNullChar 区切りで得られます。
バッファが足りない場合の戻り値は nSize - 2 です。

セクションとキーを Nothing にすれば、セクションの一覧が vbNullChar 区切りで得られます。
バッファが足りない場合の戻り値は nSize - 2 です。



手元の環境では、
 Dim ini As String = "C:\Windows\INF\.NETFramework\corperfmonsymbols.ini"
に サイズ 1.44MB の巨大な ini ファイル(4250 エントリ)があったので、
処理効率を無視して、全項目を GetPrivateProfileString で列挙させてみたところ、
4.5 秒もの時間が必要でした。

一括取得が目的の場合、サイズによっては、
System.IO.File.ReadLines で自力解析した方が早いかも知れません。


Private Declare Unicode Function GetPrivateProfileString Lib "kernel32" Alias "GetPrivateProfileStringW" (
  <MarshalAs(UnmanagedType.LPWStr)> ByVal lpAppName As String,
  <MarshalAs(UnmanagedType.LPWStr)> ByVal lpKeyName As String,
  <MarshalAs(UnmanagedType.LPWStr)> ByVal lpDefault As String,
  <MarshalAs(UnmanagedType.LPWStr, SizeParamIndex:=4)> ByVal lpReturnedString As String,
  ByVal nSize As Integer,
  <MarshalAs(UnmanagedType.LPWStr)> ByVal lpFileName As String) As Integer

Public Function GetAllIniSettings(ByVal lpFileName As String) As Dictionary(Of String, Dictionary(Of String, String))
  Const DefaultBufferSize As Integer = 4096
  'Const DefaultBufferSize As Integer = 256 * 1024

  Dim sections As Dictionary(Of String, Dictionary(Of String, String))
  Dim secSize As Integer = DefaultBufferSize
  Do
    Dim secBuf As New String(ChrW(0), secSize)
    Dim secLen As Integer = GetPrivateProfileString(Nothing, Nothing, Nothing, secBuf, secSize, lpFileName)
    If secLen = secSize - 2 Then
      secSize += DefaultBufferSize
    Else
      sections = secBuf.Substring(0, secLen).Split(vbNullChar.ToArray(), StringSplitOptions.RemoveEmptyEntries).ToDictionary(Function(k) k, Function() New Dictionary(Of String, String))
      Exit Do
    End If
  Loop

  For Each section In sections
    Dim keySize As Integer = DefaultBufferSize
    Do
      Dim keyBuf As New String(ChrW(0), keySize)
      Dim keyLen As Integer = GetPrivateProfileString(section.Key, Nothing, Nothing, keyBuf, keySize, lpFileName)
      If keyLen = keySize - 2 Then
        keySize += DefaultBufferSize
      Else
        For Each key In keyBuf.Substring(0, keyLen).Split(vbNullChar.ToArray(), StringSplitOptions.RemoveEmptyEntries)
          Dim valSize As Integer = DefaultBufferSize
          While True
            Dim valBuf As New String(ChrW(0), valSize)
            Dim valLen As Integer = GetPrivateProfileString(section.Key, key, Nothing, valBuf, valSize, lpFileName)
            If valLen = valSize - 1 Then
              valSize += DefaultBufferSize
            Else
              section.Value.Add(key, valBuf.Substring(0, valLen))
              Exit While
            End If
          End While
        Next
        Exit Do
      End If
    Loop
  Next

  Return sections
End Function
引用返信 編集キー/
■89081 / inTopicNo.14)  Re[6]: GetPrivateProfileSectionについて
□投稿者/ uMS (1回)-(2018/10/31(Wed) 01:13:07)
解決しました、皆様ありがとうございました。
解決済み
引用返信 編集キー/

このトピックをツリーで一括表示


トピック内ページ移動 / << 0 >>

このトピックに書きこむ