mieki256's diary



2024/01/14() [n年前の日記]

#1 [prog] C言語でiniファイルの読み書きをしたい

C/C++ と OpenGL を使って、Windows用のスクリーンセーバを作りたい。しかし、スクリーンセーバの設定パラメータを、どこにどうやって保存しようか…。

一般的なスクリーンセーバは、えてしてWindowsのレジストリに設定を記録するらしいけど、レジストリの読み込みはともかく、書き込みなんて怖くてやりたくない。ここは一つ、iniファイルの読み書きで勘弁してもらえないだろうか。

でも、C/C++ でiniファイルの読み書きってどうやったらいいんだろう。ということでそのあたりを調べて実験してみた。

環境は、Windows10 x64 22H2 + MinGW (gcc 6.3.0)。

WritePrivateProfileStringを使う :

ググったところ、iniファイルの読み書きについては、Windows なら WritePrivateProfileString()、GetPrivateProfileString() といった関数が使えるらしい。

_INIファイル(Win32API)(C言語) - 超初心者向けプログラミング入門
_iniファイルを読み込む - わびさびサンプルソース

Windowsに特化してるあたりがなんだかちょっと気になるけれど、これがもし Linux なら、ini ファイルではなく .xxxxrc 等のファイルに設定を保存しそうではあるし、iniファイルにアクセスする時点で、そのプログラムは Windows上でしか動かさないやろ、という気もするのでこの際使ってしまうことにした。

そんな感じで、こうなった。実行ファイルと同じ場所(ディレクトリ)に test.ini が無かったら作成して、その test.ini の内容を読み込んで printf() で出力するだけのプログラム。

_01_write_ini.c
// ini file read and write sample

#include <stdio.h>
#include <stdlib.h>
#include <shlwapi.h>
#include <windows.h>

#define INIFILENAME "test.ini"
#define SECNAME "ssstarsgl_config"

// global work
int wait = 15;
int speed = 1000;
int number = 500;
int fps_disp_enable = 0;

int main(void)
{
  char cdir[MAX_PATH];
  char filepath[MAX_PATH];

  // get current directory
  GetCurrentDirectory(MAX_PATH, cdir);

  // create save file path
  PathCombine(filepath, cdir, INIFILENAME);

  printf("Current Directory : %s\n", cdir);
  printf("ini file path : %s\n\n", filepath);

  if (!PathFileExists(filepath))
  {
    // Not found ini file
    printf("Not found %s\n", filepath);
    printf("Create %s\n\n", filepath);

    char buf[256];

    // create/write ini file
    sprintf(buf, "%d", wait);
    WritePrivateProfileString(SECNAME, "wait", buf, filepath);

    sprintf(buf, "%d", speed);
    WritePrivateProfileString(SECNAME, "speed", buf, filepath);

    sprintf(buf, "%d", number);
    WritePrivateProfileString(SECNAME, "number", buf, filepath);

    sprintf(buf, "%d", fps_disp_enable);
    WritePrivateProfileString(SECNAME, "fps_disp_enable", buf, filepath);
  }

  if (!PathFileExists(filepath))
  {
    printf("Not found %s\n\n", filepath);
    return -1;
  }
  else
  {
    printf("Found %s\n", filepath);

    // read ini file
    printf("Read ini file\n");
    wait = GetPrivateProfileInt(SECNAME, "wait", -1, filepath);
    speed = GetPrivateProfileInt(SECNAME, "speed", -1, filepath);
    number = GetPrivateProfileInt(SECNAME, "number", -1, filepath);
    fps_disp_enable = GetPrivateProfileInt(SECNAME, "fps_disp_enable", -1, filepath);

    // dump results
    printf("wait  = %d\n", wait);
    printf("speed = %d\n", speed);
    printf("number = %d\n", number);
    printf("fps_disp_enable = %d\n", fps_disp_enable);
  }

  return 0;
}

_Makefile
01_write_ini.exe: 01_write_ini.c
    gcc $< -o $@ -lshlwapi -Wall -O3

.PHONY: clean
clean:
    rm -f *.exe
    rm -f *.o
    rm -f test.ini

make でビルドして 01_write_ini.exe を作る。

DOS窓(cmd.exe)上で 01_write_ini.exe を実行すると、以下のような出力をして、test.ini という iniファイルも作成された。
> 01_write_ini.exe
Current Directory : D:\home\prg\c_lang\gcc\ini_read_write\take1
ini file path : D:\home\prg\c_lang\gcc\ini_read_write\take1\test.ini

Not found D:\home\prg\c_lang\gcc\ini_read_write\take1\test.ini
Create D:\home\prg\c_lang\gcc\ini_read_write\take1\test.ini

Found D:\home\prg\c_lang\gcc\ini_read_write\take1\test.ini
Read ini file
wait  = 15
speed = 1000
number = 500
fps_disp_enable = 0

test.ini の内容は以下のような感じ。

_test.ini
[ssstarsgl_config]
wait=15
speed=1000
number=500
fps_disp_enable=0

再度実行すると、test.ini が存在するので、test.ini を読み込んで内容の表示だけをする。
> 01_write_ini.exe
Current Directory : D:\home\prg\c_lang\gcc\ini_read_write\take1
ini file path : D:\home\prg\c_lang\gcc\ini_read_write\take1\test.ini

Found D:\home\prg\c_lang\gcc\ini_read_write\take1\test.ini
Read ini file
wait  = 15
speed = 1000
number = 500
fps_disp_enable = 0

少し解説 :

分かった範囲で、少し解説。

  • 実行している exeファイルの場所(ディレクトリ)を取得するには、GetCurrentDirectory() を使う。
  • ディレクトリのPATHに、ファイル名を結合したい時は、PathCombine() を使う。

_Win32APIでパス名とファイル名を連結する - プログラムを書こう!
_PathCombine

  char filepath[MAX_PATH];

  // get current directory
  GetCurrentDirectory(MAX_PATH, cdir);

  // create save file path
  PathCombine(filepath, cdir, INIFILENAME);

  printf("Current Directory : %s\n", cdir);
  printf("ini file path : %s\n\n", filepath);


ファイルの存在チェックは、PathFileExists() が使える。

_PathFileExists - Windows APIの部屋

  if (!PathFileExists(filepath))
  {
    // Not found ini file
    // ...
  }
  else
  {
    // Found ini file
    // ...
  }

注意点。PathCombine() や PathFileExists() を使うには、#include <shlwapi.h> が必要らしい。また、gcc/g++ に -lshlwapi を渡してライブラリをリンクしないといけない。


iniファイルへの書き込みは、WritePrivateProfileString() を使う。もし、指定したiniファイルが存在しなかったら、自動でiniファイルを作成して書き込んでくれるらしい。
WritePrivateProfileString("セクション名", "キー名", "キーの文字列", "iniファイルのPATH");
    // create and write ini file

    char buf[256];

    sprintf(buf, "%d", wait);
    WritePrivateProfileString(SECNAME, "wait", buf, filepath);

    sprintf(buf, "%d", speed);
    WritePrivateProfileString(SECNAME, "speed", buf, filepath);

    sprintf(buf, "%d", number);
    WritePrivateProfileString(SECNAME, "number", buf, filepath);

    sprintf(buf, "%d", fps_disp_enable);
    WritePrivateProfileString(SECNAME, "fps_disp_enable", buf, filepath);


iniファイルからの読み込みは、GetPrivateProfileString() を使って文字列として読み込むのが一般的らしいけど、今回は Int値を読み込めれば十分なので、Int値を返してくる GetPrivateProfileInt() を使って済ませることにした。この GetPrivateProfileInt()、もし、指定したキー名が無かったときは、デフォルト値を返すらしい。
int GetPrivateProfileInt("セクション名", "キー名", デフォルト値, "iniファイルPATH")
    // read key value from ini file

    wait = GetPrivateProfileInt(SECNAME, "wait", -1, filepath);
    speed = GetPrivateProfileInt(SECNAME, "speed", -1, filepath);
    number = GetPrivateProfileInt(SECNAME, "number", -1, filepath);
    fps_disp_enable = GetPrivateProfileInt(SECNAME, "fps_disp_enable", -1, filepath);

とりあえず、これで iniファイルの読み書きはできそう。

ただ、このプログラム、英数字のみのPATHにしか対応できない気もする…。PATHを格納するバッファを char で用意してるので、日本語文字列を含むディレクトリやファイル名には対応できないのではないかな…。いや、日本語文字列でフォルダを作ってその中で試したら、一応動いているようではあるけれど…。

以上、1 日分です。

過去ログ表示

Prev - 2024/01 - Next
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30 31

カテゴリで表示

検索機能は Namazu for hns で提供されています。(詳細指定/ヘルプ


注意: 現在使用の日記自動生成システムは Version 2.19.6 です。
公開されている日記自動生成システムは Version 2.19.5 です。

Powered by hns-2.19.6, HyperNikkiSystem Project