none
VC++での文字列の扱い RRS feed

  • 質問

  • VC++での文字列の扱いが分からないので、

    http://msdn.microsoft.com/ja-jp/library/ms235631(v=VS.90).aspx

    のvisual studio2008のところを見てみたのですが、やっぱり意味がわかりませんでした。プログラムはMFCでやってます。OSはXPです(いまだに)。

    (1)それぞれの型について解説したページは、どこかにないでしょうか?それぞれの型の利点、欠点などをまとめた一覧表のようなものが見たいと思っています。(型の種類が多くて、どういうときにどれを使うのか意味不明です。CStringとcharくらいしか使ったことが無い。)

    (2)入力される文字が数字と限定されている場合に、その文字列(つまり数字列)を数値として(int型など)扱いたい場合、どうするのが良いのでしょうか?動作保証までは求めませんが、だからと言ってビルドエラーが出てしまうサンプルでは検討が続けられず困っています。ビルドが通って、ある程度動くサンプルプログラムがほしいと思います。

    実は、とりあえずchar*にでも変換できればあとはなんとでもなるだろうと思い、冒頭に書いたURLの中にある

        // Convert to a char*
       CString orig("Hello, World!");
        const size_t newsize = 100;
        char nstring[newsize];
        strcpy_s(nstring, orig);
        strcat_s(nstring, " (char *)");
        cout << nstring << endl;

    をコピペしてみたのですが、動作確認以前にビルドエラーで止まってしまいます。そのエラーは以下の通りで、そもそも関数の引数の型が違うと言われてしまっています。

    error C2665: 'strcpy_s' : 2 オーバーロードのどれも、すべての引数の型を変換できませんでした
            c:\Program Files\Microsoft Visual Studio 9.0\VC\include\string.h(73): 'errno_t strcpy_s<100>(char (&)[100],const char *)' の可能性があります。
            引数リスト '(char [100], CString)' を一致させようとしているとき

     

    以上です。よろしくお願いします。

    • 移動 山本春海 2010年11月4日 5:13 より適切なカテゴリに移動 (移動元:Visual Studio 共通 フォーラム)
    2010年11月2日 14:55

回答

  • winUser.h(8590):#define LoadBitmap  LoadBitmapW

    という定義がされているのですが、ソースコード上でW付きと無しを書き分けて、意味があるのでしょうか?どっちも同じ結果になりそうです。

    また、最初は、私もLoadBitmap を使っていたのですが、いつだったかビルドの時にワーニングが出て、「その関数はセキュリティが弱いから、W付きのほうを使え」という内容だったのですが、これは無視して良いのでしょうか?

    その行だけ見ても本当の意味がわからないと思いますよ。
    その行の前後まで含めてみれば、プロジェクトの設定で切り替えられるからくりもわかるはずです。
    WとかAがついてるものはそれぞれがUnicode用とマルチバイトコード用です。
    UNICODEという定義が有るかで切り替えられるようになっています。

    つまり、LoadBitmapで書いておけば、その定義でダイナミックに切り替わると言う事です。
    他のAPIに関しても文字列を扱うような物に関しては同じような定義になっていると思います。

    私の環境(VisualStudio2008)ではワーニングは出ませんけれど、どういう記述をした時に出たんでしょう?
    通常、セキュリティ系のワーニングが出るのはstr系のランタイム関数で_s付きの関数を使えという時に
    出ていたと思うのですけれど。

     


    解決した時は、参考になったレスポンスの所にある[回答としてマーク]ボタンをクリックしてスレッドを締めましょう。
    • 回答としてマーク 山本春海 2010年11月29日 4:21
    2010年11月5日 7:53
  • ソースコード上でW付きと無しを書き分けて、意味があるのでしょうか?どっちも同じ結果になりそうです。

    マルチバイト文字セットを使っているときにあえて、Unicode 文字セットの関数を呼び出したいとか、TCHAR 思想の #define がない API から受け取った文字列を渡すときとかでしょうか。
    文字列を扱う上で明確な目的があれば、W をつけることはあり得ます。
    結果としては同じになるかもしれませんし、プロジェクトの設定によっては違うかもしれません。

    ただ、そのソースコードを誰かと共有する、プロジェクトの設定を変えることがあるといった事情がない限り、TCHAR を深く考えなくて良いとは思います。

    また、最初は、私もLoadBitmap を使っていたのですが、いつだったかビルドの時にワーニングが出て、「その関数はセキュリティが弱いから、W付きのほうを使え」という内容だったのですが、これは無視して良いのでしょうか?

    見た覚えがありません。
    もう一度、同じようなコードを書くなど、再確認してみてください。


    質問スレッドで解決した場合は、解決の参考になった投稿に対して「回答としてマーク」のボタンを押すことで、同じ問題に遭遇した別のユーザが役立つ投稿を見つけやすくなります。
    • 回答としてマーク 山本春海 2010年11月29日 4:21
    2010年11月5日 13:44
    モデレータ
  • (1)それぞれの型について解説したページは、どこかにないでしょうか?それぞれの型の利点、欠点などをまとめた一覧表のようなものが見たいと思っています。(型の種類が多くて、どういうときにどれを使うのか意味不明です。CStringとcharくらいしか使ったことが無い。)

    申し訳ないですが、これは探してないので有無はわかりません。
    以下は文字セットについて簡単に書いたものです。

    マルチバイト文字セットは文字によって 1 バイトだったり、複数バイトだったりします。
    この文字セットの場合、OS の設定によって SHIFT JIS だったり、Windows-1252 だったりするので、実行環境によっては正しく文字を扱えません。
    (Unicode に対応でないプログラムの言語 によってプログラムの動作に影響を受ける)

    Unicode 文字セットは 1 文字が 原則 2 バイトになります。
    現在の Windows プログラムではこちらが主流になりつつあります。
    ただし、特殊な文字(サロゲートペア・合成文字など)は複数文字分のバイトを消費して、1 文字を表すことがあります。

    コピペしてみたのですが、動作確認以前にビルドエラーで止まってしまいます。そのエラーは以下の通りで、そもそも関数の引数の型が違うと言われてしまっています。

    これはサンプルの先頭に書いてあるコマンドラインでコンパイルすることを前提としています。
    新しいプロジェクトに貼り付けただけの場合、プロジェクトの設定、プリプロセッサ定数の影響を受けるため、前提が異なり、ビルドエラーを招きます。

    今回の場合、プロジェクトのプロパティ、全般の設定、文字セットを Unicode 文字セットからマルチバイト文字セットに変えればビルドは通ると思いますが、文字セットの違いなどを学んだ上で、Unicode 文字セットの採用も徐々に考えていってください。

    Unicode 文字セットでは CString の中身は wchar_t ベースになり、マルチバイト文字セットでは CString の中身は char ベースになります。
    前述のサンプルでは後者の CString が char ベースであることを期待しているが、新規に作ったプロジェクトのデフォルトが Unicode 文字セットになるため、前者の CString が wchar_t ベースになり、型が合わないことになっています。


    質問スレッドで解決した場合は、解決の参考になった投稿に対して「回答としてマーク」のボタンを押すことで、同じ問題に遭遇した別のユーザが役立つ投稿を見つけやすくなります。
    • 回答の候補に設定 山本春海 2010年11月22日 4:23
    • 回答としてマーク 山本春海 2010年11月29日 4:21
    2010年11月2日 22:19
    モデレータ
  • まず、はっきりとVC++の話と分かっているのであれば、VC++のフォーラムで質問するべきかなと思います。
    あちらで質問した方がレスもつきやすいと思いますし。

    えーと、どの程度までならわかりますか?

    C++言語の文法はしっかり理解できていますか?
    自分でクラスを作成して使う事はできていますか?

    文字列を扱う場合のクラスですが、いきなり全ての型を覚えておく必要は無いと思っています。
    さしあたってわかっていた方が良さそうなのは、char * wchar_t* 、CStringくらいがわかっていれば十分です。
    文字列に関しては、UnicodeとMultiByteCodeの2種類が有り、扱いが若干変わります。
    この点については勉強してみてください。とてもここでは書き切れないくらいのネタがあります。
    他の部分に関しては必要が出てきた時点で勉強しても良いと思いますよ。

    CComBSTR、basic_string、System.Stringなんかはまだ最初の勉強段階では多分使いません。
    それぞれの内容を調べてみるのは良いと思いますけれど、現段階で分からないなら先延ばししても
    良いと思います。後で必要になった時にああこういう型があったなあと思いだせる程度良いかな。

    最近のコンパイラはエラーメッセージを日本語で出してくれますし、一応意味がわかる内容で表示してくれています。
    但し、エラー内容はコンパイラ目線なのでそれに慣れる必要はあります。

    例えば、書かれているエラーですけれど、オーバーロードって分かりますか?
    関数の多重定義のことなんですけれど。
    多重定義の意味がわかるとエラーの内容がわかるはずです。
    勉強できるネタはいたる所にありますよ。エラーメッセージもまた勉強のネタです。
    オーバーロードだけでも結構なネタになります。
    で、C++言語でオーバーロードは大切な概念の一つです。

    もし、C++言語の文法自体が怪しいなら入門書を使って勉強する事をお勧めします。
    C++言語の文法の知識なしにVC++は勉強できません。
    VC++は開発環境の名前で開発言語の名前ではありません。


    解決した時は、参考になったレスポンスの所にある[回答としてマーク]ボタンをクリックしてスレッドを締めましょう。
    • 回答の候補に設定 山本春海 2010年11月22日 4:27
    • 回答としてマーク 山本春海 2010年11月29日 4:22
    2010年11月3日 1:54
  • が、出てくる(つまり、引数は両方ともchar*でCSrtingやCSrting*が出てこない)のですが、CStringから、charへ変えたいのに、関数へ渡す第2引数がキャストもせずに直接

    strcpy_s(nstring, orig);
    と書いてあるのが感覚的に理解できていないのですが、

    「Unicode 文字セットでは CString の中身は wchar_t ベースになり、マルチバイト文字セットでは CString の中身は char ベースになります。」

    ということなのでしょうか?

    この辺は、C++言語についてもう少し突っ込んで勉強する必要があると思います。

    C++言語には演算子のオーバーロードと言うのがあるのはご存知ですか?
    型変換を行なうキャストもまた演算子として捉える事ができ、CStringクラスにはPCXSTRへのキャストが
    定義されています。PCXSTRはconst 文字列へのポインタです。この場合、使用する文字コードによって
    charのポインタになったり、wchar_tのポインタになったりすると思ってください。
    (Visual C++ 2005以降、CStringはテンプレートクラスとして定義されていて実装が階層的に
    行なわれています。キャスト演算子PCXSTRをMSDN上で探すにはその階層を辿る必要があります)
    この場合、このキャスト演算子が定義されているので暗黙的に型変換が行なわれます。
    但し、この暗黙と言うのはコンパイラが利用するべき演算子を一意に特定できる場合だけです。
    特定できない場合はエラーが出るので明示的にキャストする必要が出てきます。

    使用する文字コードの指定によってCStringが内部で扱う文字列のデータ型をcharにしたり、wchar_tにしたり
    する事とキャストせずに指定できる事の間には直接の関係は有りません。
    暗黙の型変換に使用できる演算子が存在しているかどうかが鍵になります。

    オーバーロードとオーバーライドに関してはC++言語の特徴的な部分になりますので
    どういうものであるのかと同時に使う上での注意点を理解しておく必要があります。
    ご本人がどのような方法でC++言語の文法を勉強されたのか分かりませんが、
    C++言語の入門書で一通りの内容を通して勉強されていないのでしたら
    今からでも通しで勉強される事をお勧めします。

    で、オーバーロード云々の話ですが、
    C++言語では関数の引数の型や個数の違う同名の関数を複数定義できることですよね。
    この事からエラーメッセージは、strcpy_sと言う関数名で多重定義された幾つかの関数の中に
    指定された引数の型と数の条件に合う関数はなかったというエラーになるわけです。
    平たく言うとそんな関数は存在しないって事ですね。
    HELPを見るとわかりますが、書かれているような使い方でstrcpy_sを使う場合、
    引数は3つ必要になります。従って該当する関数は見つからないとなるわけです。
    C言語ですとオーバーロードが無いので引数が間違っていますと出せますが、
    C++言語の場合、オーバーロードの機能があるのでそういうメッセージにならないわけです。

    ちなみにですが、VS2010の方であれば、引数3個の関数呼び出しを使っているようですよ。
    悲しいかな、MSDNのサンプルと言えど完璧と言うわけでは無いのでそういう場合は
    こういう場で質問してみるのはOKだと思います。
    その時に自分はこう思うのだけれど、まで提示できればベストですね。


    解決した時は、参考になったレスポンスの所にある[回答としてマーク]ボタンをクリックしてスレッドを締めましょう。
    • 回答としてマーク 山本春海 2010年11月29日 4:21
    2010年11月3日 4:38
  • DrawItem( LPDRAWITEMSTRUCT lpDrawItemStruct)
    とか
    LRESULT MessageFunc(WPARAM valWPARAM, LPARAM valLPARAM);
    とか
    ReceiveThread(LPVOID arg)
    というのがあって、
    引数のところに、LPDRAWITEMSTRUCT、WPARAM、LPVOIDのような大文字で定義された型があります。上記に限らず、大文字で定義された型がそこらじゅうにたくさん出てきますが、これらは全部「演算子のオーバーロード」ですか?

    いいえ、単純に全て大文字だからと言うわけではないです。
    この辺の型はそもそもC言語の頃からあったものですし、オーバーロードとは関係有りません。
    この辺はデータ型を使いやすくしたり、状況によって切り替える事ができるようにする為に別名をつけている物が多いです。
    例えば、LPVOIDはvoid *の別名です。
    そういう意味で言うとPCXSTRもデータ型の名前です。
    実際にオーバーロードされた演算子を表現する場合は、キャスト演算子の場合、変換される型と同じ名前で作成されるので
    結果的に同じになっているだけです。全て大文字と言う事とキャスト演算子のオーバーロードとの関係はありません。
    そこは分けて考えてください。
    例えばですが、あるクラスCDataにint型にキャストする演算子を定義する事だってできます。
    その場合、int CData::operator int() const;と言う風に定義ができます。
    大文字である事が必須ではない事がわかると思います。

     

    文字列の扱いという話からは少しそれるのですが、例えば、

    >使用する文字コードの指定によってCStringが内部で扱う文字列のデータ型をcharにしたり、wchar_tにしたり
    >する事とキャストせずに指定できる事の間には直接の関係は有りません。
    >暗黙の型変換に使用できる演算子が存在しているかどうかが鍵になります。

    そういうことなのですね。知りませんでした。

    >C++言語の入門書で一通りの内容を通して勉強されていないのでしたら
    >今からでも通しで勉強される事をお勧めします。

    いちおう、基礎的な本は持っています。ただ、演算子の扱い(オーバーロード)の頁は読み飛ばしていました。暫くの間は、そんなところを使うと思っていなかったので・・・こんど読みます。

    実は、MFCを使いこなす為にはC++言語の文法と概念的な部分の理解が不可欠です。
    クラスの概念、派生、仮想関数、純粋仮想クラス、オーバーロード、オーバーライド等々。
    MFCはクラスライブラリなのでこういった知識が土台に無いと使いこなせませんし、
    どうして派生する必要があるのかとか、どのように派生クラス側で定義したら良いのか等の
    実装レベルの話についていけない状態になります。
    コンパイルエラー等に対処するにも文法の知識は必要です。
    一度に全てを理解するのは無理でも一通りは読んでおいてこの内容は確か書いてあったな
    くらいの状態にはしておいた方が良いですよ。
    単なる入門書ではなくてコンソールプログラムを書きながら実際に演習をするタイプの本が
    こういう勉強には向いています。オブジェクト指向の考え方の部分まである程度カバーできていると
    MFCを実際に使ってプログラミングする時でもなぜそういう実装になっているのかが
    ある程度わかるようになります。そうすれば、基本的な使い方から発展させて使う事が
    できるようになると思います。

     


    解決した時は、参考になったレスポンスの所にある[回答としてマーク]ボタンをクリックしてスレッドを締めましょう。
    • 回答としてマーク 山本春海 2010年11月29日 4:21
    2010年11月3日 6:37
  • char で言う、char *Test = "Hello, World!" という定義は、CStringのときはどう書けばよいんでしょうか?

    何を目的にするか、どういったレベルで解決とするか次第でしょうか。

    案1:プロジェクトの設定を マルチバイト文字セット に変える。
    案2:CString を使わず、char 型ベースの CStringA を使う。
    案3:wchar_t 型ベースの関数 wcscpy_s に乗り換え、wchar_t 型ベースでコードを書く。
    案4:きちんと変換する。CT2A クラスを使うなど。

    この段階ではややこしくなるので、TCHAR の概念は省略します。
    また、それぞれの方法の詳細にはこの投稿の段階では踏み込みません。

    どこかにサンプルソースがありませんか?文字の扱い方を除く他のものはたいがいネットで見つかるのに、なぜか文字列の扱いに関するものは皆無に等しいです(あるにはあるが、ビルドが通らなかったりちゃんと動かないものばかり)

    ややこしいからこそ、サンプルが少ないのでしょう。
    ビルドが通らないのは、プロジェクトの設定が悪いからかもしれませんので、「ちゃんと動かないものばかり」と断じる前に検証しましょう。

    Visual C++ 2005 から新規に作ったプロジェクトは、wchar_t 型ベースの文字列を基本とします。
    CString が wchar_t 型ベースとなるためです、char 型ベースのサンプルと CString の組み合わせはうまく動作しません。
    文字セットの概念をとりあえず忘れて試したいだけであれば、最初にも書きましたが、プロジェクトの設定の文字セットを、Unicode 文字セットからマルチバイト文字セットに変えてください。


    質問スレッドで解決した場合は、解決の参考になった投稿に対して「回答としてマーク」のボタンを押すことで、同じ問題に遭遇した別のユーザが役立つ投稿を見つけやすくなります。
    • 回答としてマーク 山本春海 2010年11月29日 4:21
    2010年11月3日 13:47
    モデレータ
  • 「文字セットの概念をとりあえず忘れて試したい」というレベルなので、実は昼間に試してみました。しかし、別の場所で文字列定義をしているところが幾つもあり、こんどはそっちがビルドエラーで引っかかってきてしまうので、この方法で、(私にとって)臭いものに蓋をする野望は失敗に終わりました。ここでいう、「別の場所での文字列定義」は、普通のchar*配列でプログラム上でリストボックスやコンボボックスを表示するために使うような、文字列データが定義された配列です。(けっこう定義数が多い)

    ビルドエラーになると言うことは、wchar_t 型ベースの文字列前提の処理がすでに存在していると言うことですね。
    char 型ベースの文字列で進めようとすると、今回エラーになった箇所とこれから作る箇所のつなぎ目で再び問題が発生するように思えます。
    当面の作戦としては、wchar_t 型ベースの文字列で統一していった方がいいかもしれません。

    (文字列定義の部分が気になりますが、話を簡単にするため一旦放置します)

    目的は、エディットボックスに記入されたCString型の文字列(ただし数字のみで構成された文字列)を、プログラム上でint型として扱いたいというような、単に文字列を数値へ変換したいだけです。その前段階として、CStringをcharに変えようと思いました。ですから、他に方法があるならそれでも良いと思っていますので、案2と案3を少し試してみようかと思っています。都合よのい表現があったので使わせていただきますが、「文字セットの概念をとりあえず忘れて試したいだけ」というレベルです。

    今の設定の GetWindowText で取得した場合、wchar_t 型ベースの CString になります。
    このため、wchar_t 型ベースで処理していった方が問題は少ないように思えます。
    char 型ベースの関数を使おうとすると、変換が必要になって、煩雑になると思われます。

    wchar_t 型ベースの関数名がわからない場合は、MSDN で調べましょう。
    たとえば、strcpy_s 関数の場合、次のページの下の方に wcscpy_s が出てきます。
    http://msdn.microsoft.com/ja-jp/library/td1esda9(VS.80).aspx

    (本当には tchar.h (_tcscpy_s ) を使った方が良いですが、今の問題が解消するまでは一旦置いておきます。解消したら、tchar.h だと何が良いのかとか調べてみてください)


    質問スレッドで解決した場合は、解決の参考になった投稿に対して「回答としてマーク」のボタンを押すことで、同じ問題に遭遇した別のユーザが役立つ投稿を見つけやすくなります。
    • 回答としてマーク 山本春海 2010年11月29日 4:21
    2010年11月3日 21:52
    モデレータ
  • 後回しにしてはと言う話になっていますけれど、一応書いておきますね。

    目的は、エディットボックスに記入されたCString型の文字列(ただし数字のみで構成された文字列)を、プログラム上でint型として扱いたいというような、単に文字列を数値へ変換したいだけです。その前段階として、CStringをcharに変えようと思いました。ですから、他に方法があるならそれでも良いと思っていますので、案2と案3を少し試してみようかと思っています。 都合よのい表現があったので使わせていただきますが、「文字セットの概念をとりあえず忘れて試したいだけ」というレベルです。
    そのほうが私も助かります。TCHAR もどこかのHPで見たことはあるのですが、ざっと読んでみて下手にかかわるのは嫌な予感がしたので読み飛ばしています。文字列操作は私の実験、検討材料にとっておまけ的な機能だということもあり、あまり深く関わらないほうがよさそうです。

    TCHARに関してはちゃんと理解しないと拙いような気がします。VC++6.0の頃からあった概念のはずですし。
    以前のプロジェクトでは、扱う文字コードの規定値がマルチバイトコードになっていて、最近ではUnicodeになっているのでTCHARを使わずにコードを書いてしまうとその部分の切替が自動で出来ないのでどちらかに合わせて書き直す必要が出てきます。
    この部分の切替をダイナミックに行う為のTCHARとTCHARを扱う関数群になるのでVisual C++を使うのであれば、おさえておかないとだめです。プロジェクトの設定の切替でダイナミックに切り替えてしまってかまわない部分の記述に関してはTCHAR系に統一してしまった方が良いですよ。MFCのクラスもプロジェクトの設定でダイナミックに切り替わるようになっているのでTCHARで書くようにしておけば、特に意識する必要はないです。
    リテラルの文字列に関してもTCHARの文字列として記述できますからそうしておけばプロジェクトの設定だけで切替が可能です。_T("ああ")と言う風に書くだけですし。一度分かってしまえば、そこまでややこしい話では無いと思いますよ。文字コードの話を深くしていくと話が長くなるのでこの場での説明は厳しいかもしれませんけれど。


    「文字セットの概念をとりあえず忘れて試したい」というレベルなので、実は昼間に試してみました。しかし、別の場所で文字列定義をしているところが幾つもあり、こんどはそっちがビルドエラーで引っかかってきてしまうので、この方法で、(私にとって)臭いものに蓋をする野望は失敗に終わりました。ここでいう、「別の場所での文字列定義」は、普通のchar*配列でプログラム上でリストボックスやコンボボックスを表示するために使うような、文字列データが定義された配列です。(けっこう定義数が多い)

    正にこういうことが起こらないようにする為のTCHARですから、TCHARで書き換えてしまってプロジェクトの設定の方で文字コードの切替をした方が良いと思いますよ。
    プロジェクトの文字コードの設定に関わらず固定にしたい場合だとTCHARを使わずに直接指定することになりますけれど、その場合はプロジェクトの文字コードの設定に合わせてコード変換する必要が出てくるケースもあると思います。
    文字コードの問題はどちらにしてもついてまわる話なので、私は放置しないで確認した方が良いと思います。
    むしろ、今回の事で再確認ができてよかったと思いますよ。


    解決した時は、参考になったレスポンスの所にある[回答としてマーク]ボタンをクリックしてスレッドを締めましょう。
    • 回答としてマーク 山本春海 2010年11月29日 4:22
    2010年11月4日 0:28
  • 1.文字セットを意識しないで、
    2.サンプルコードをコンパイルしたい。

    ということですが、古くからの多くのサンプルは概ねMBCSを前提に書かれています。
    一方、C++ネイティブコンパイラのデフォルトの設定はUnicodeです。
    なので、ソースを一切変更せずにコンパイルを通したいのなら。
    プロジェクトのプロパティで、「マルチバイト文字セットを使用する」
    に設定しなおすしかありません。初心者にはこの方がわかりやすいかもしれませんね。
    Unicode化はその後でも仕方ないかもしれません。

    さて、表面上はオプションの変更にすぎませんが、Unicodeに設定するというのは、
    文字コードが変わるという以上に劇的な変更となります。
    個人的には、よくもTCHARで吸収できてるもんだねぇと感心するほどです。
    また、Unicode化しても、「ファイルにASCIIテキストで出力」などの要望が
    でた瞬間にMBCSも意識しなければなりません。この辺は慣れが必要だと考えます。
    とりあえず。
    http://msdn.microsoft.com/ja-jp/library/06b9yaeb(v=VS.90).aspx
    あたりを一読して心にとめておいて、練習はMBCSでやるというのを
    自分はお勧めします。

     

    • 回答としてマーク 山本春海 2010年11月29日 4:21
    2010年11月4日 9:38
  • 試しにプロジェクトの設定をマルチバイトに変えてビルドしてみたら、それはそれでエラーが出ますので単にTCHARだけが解決すれば良いというものではなかったです。

    LoadBitmapWなどのW付き関数をたくさん使っていました。Wが確か、なんかの文字コード依存を意味する記号だと思います。これに関連しては、
    「'LoadImageW' : 2 番目の引数を 'LPSTR' から 'LPCWSTR' に変換できません。(新しい機能 ; ヘルプを参照)」
    というのも出ています。が、今回はユニコードでビルドが通ったのでこれでいいです。(そのうち必要が出てきたら、考えます)

    えーと、ウィザードで出力した雛形に入ってましたか?
    人の手で追加されている部分の話をしているのであれば、それはそのコードを書いた人の責任です。
    ちゃんとLPCTSTRを引数にしたLoadBitmapやLoadImage関数は存在しているのでそっちを使えば、
    本当にダイナミックに切替が可能です。

    これはあくまでもプログラム内で扱う文字コードをどちらかに統一できる場合の話なので
    万能と言うわけでは有りませんが、少なくともプロジェクトの設定の部分で切り替える場合には
    対応ができます。
    ポイントは文字列を扱う関数に関してはTCHARを扱う為の物を使用する。
    リテラル文字列はTCHARの文字列として定義する。
    くらいです。MFCは最初からプロジェクトの設定でダイナミックに切り替わるように出来ています。
    インテリセンスがそこまで考えてくれていないケースがあるのでインテリセンスで出てきた関数名を
    そのまま使ってしまうと言われているようなケースが起こりえます。
    例えば、Unicode指定時にLoadBitmapをインテリセンスで出そうとするとLoadBitmapWが
    出てきたりと言う話。間違ってはいないけれど、TCHARを扱う関数で書いておいた方が切替が
    簡単にできるので良いと思います。
    状況によってはあえてマルチバイトコード用やUnicode用を意図的に使う場合もありますけれど、
    これはプログラマが意図して使っているわけなので当然意味が分かって使っているはずです。
    逆に意味がわかっていないならちゃんと勉強しましょうねと言う話なのですが。

     


    解決した時は、参考になったレスポンスの所にある[回答としてマーク]ボタンをクリックしてスレッドを締めましょう。
    • 回答としてマーク 山本春海 2010年11月29日 4:21
    2010年11月5日 4:23

すべての返信

  • (1)それぞれの型について解説したページは、どこかにないでしょうか?それぞれの型の利点、欠点などをまとめた一覧表のようなものが見たいと思っています。(型の種類が多くて、どういうときにどれを使うのか意味不明です。CStringとcharくらいしか使ったことが無い。)

    申し訳ないですが、これは探してないので有無はわかりません。
    以下は文字セットについて簡単に書いたものです。

    マルチバイト文字セットは文字によって 1 バイトだったり、複数バイトだったりします。
    この文字セットの場合、OS の設定によって SHIFT JIS だったり、Windows-1252 だったりするので、実行環境によっては正しく文字を扱えません。
    (Unicode に対応でないプログラムの言語 によってプログラムの動作に影響を受ける)

    Unicode 文字セットは 1 文字が 原則 2 バイトになります。
    現在の Windows プログラムではこちらが主流になりつつあります。
    ただし、特殊な文字(サロゲートペア・合成文字など)は複数文字分のバイトを消費して、1 文字を表すことがあります。

    コピペしてみたのですが、動作確認以前にビルドエラーで止まってしまいます。そのエラーは以下の通りで、そもそも関数の引数の型が違うと言われてしまっています。

    これはサンプルの先頭に書いてあるコマンドラインでコンパイルすることを前提としています。
    新しいプロジェクトに貼り付けただけの場合、プロジェクトの設定、プリプロセッサ定数の影響を受けるため、前提が異なり、ビルドエラーを招きます。

    今回の場合、プロジェクトのプロパティ、全般の設定、文字セットを Unicode 文字セットからマルチバイト文字セットに変えればビルドは通ると思いますが、文字セットの違いなどを学んだ上で、Unicode 文字セットの採用も徐々に考えていってください。

    Unicode 文字セットでは CString の中身は wchar_t ベースになり、マルチバイト文字セットでは CString の中身は char ベースになります。
    前述のサンプルでは後者の CString が char ベースであることを期待しているが、新規に作ったプロジェクトのデフォルトが Unicode 文字セットになるため、前者の CString が wchar_t ベースになり、型が合わないことになっています。


    質問スレッドで解決した場合は、解決の参考になった投稿に対して「回答としてマーク」のボタンを押すことで、同じ問題に遭遇した別のユーザが役立つ投稿を見つけやすくなります。
    • 回答の候補に設定 山本春海 2010年11月22日 4:23
    • 回答としてマーク 山本春海 2010年11月29日 4:21
    2010年11月2日 22:19
    モデレータ
  • まず、はっきりとVC++の話と分かっているのであれば、VC++のフォーラムで質問するべきかなと思います。
    あちらで質問した方がレスもつきやすいと思いますし。

    えーと、どの程度までならわかりますか?

    C++言語の文法はしっかり理解できていますか?
    自分でクラスを作成して使う事はできていますか?

    文字列を扱う場合のクラスですが、いきなり全ての型を覚えておく必要は無いと思っています。
    さしあたってわかっていた方が良さそうなのは、char * wchar_t* 、CStringくらいがわかっていれば十分です。
    文字列に関しては、UnicodeとMultiByteCodeの2種類が有り、扱いが若干変わります。
    この点については勉強してみてください。とてもここでは書き切れないくらいのネタがあります。
    他の部分に関しては必要が出てきた時点で勉強しても良いと思いますよ。

    CComBSTR、basic_string、System.Stringなんかはまだ最初の勉強段階では多分使いません。
    それぞれの内容を調べてみるのは良いと思いますけれど、現段階で分からないなら先延ばししても
    良いと思います。後で必要になった時にああこういう型があったなあと思いだせる程度良いかな。

    最近のコンパイラはエラーメッセージを日本語で出してくれますし、一応意味がわかる内容で表示してくれています。
    但し、エラー内容はコンパイラ目線なのでそれに慣れる必要はあります。

    例えば、書かれているエラーですけれど、オーバーロードって分かりますか?
    関数の多重定義のことなんですけれど。
    多重定義の意味がわかるとエラーの内容がわかるはずです。
    勉強できるネタはいたる所にありますよ。エラーメッセージもまた勉強のネタです。
    オーバーロードだけでも結構なネタになります。
    で、C++言語でオーバーロードは大切な概念の一つです。

    もし、C++言語の文法自体が怪しいなら入門書を使って勉強する事をお勧めします。
    C++言語の文法の知識なしにVC++は勉強できません。
    VC++は開発環境の名前で開発言語の名前ではありません。


    解決した時は、参考になったレスポンスの所にある[回答としてマーク]ボタンをクリックしてスレッドを締めましょう。
    • 回答の候補に設定 山本春海 2010年11月22日 4:27
    • 回答としてマーク 山本春海 2010年11月29日 4:22
    2010年11月3日 1:54
  • Azuleanさん ありがとうございます。

    インクルードディレクトリの検索をかけると、例えば

    errno_t __cdecl strcpy_s(_Out_z_cap_(_SizeInBytes) char * _Dst, _In_ size_t _SizeInBytes, _In_z_ const char * _Src);

    inline void __cdecl strcpy_s(_Out_cap_(_S1max) char *_S1, _In_ size_t _S1max, _In_z_ const char *_S2)

    が、出てくる(つまり、引数は両方ともchar*でCSrtingやCSrting*が出てこない)のですが、CStringから、charへ変えたいのに、関数へ渡す第2引数がキャストもせずに直接

    strcpy_s(nstring, orig);
    と書いてあるのが感覚的に理解できていないのですが、

    「Unicode 文字セットでは CString の中身は wchar_t ベースになり、マルチバイト文字セットでは CString の中身は char ベースになります。」

    ということなのでしょうか?

    2010年11月3日 3:28
  • PATIOさん、ありがとうございます。

    >まず、はっきりとVC++の話と分かっているのであれば、VC++のフォーラムで質問するべきかなと思います。
    >あちらで質問した方がレスもつきやすいと思いますし。

    言われてみればそうかも知れません。というか、このフォーラムの構造があまり分かってない気がします。自分が使っているのが、visual studio2008だったので、最初に見つかったあまり深く考えずフォーラムへ投稿させていただきました。

    >C++言語の文法はしっかり理解できていますか?
    >自分でクラスを作成して使う事はできていますか?

    >例えば、書かれているエラーですけれど、オーバーロードって分かりますか?

    3つまとめて、、ですが

    浅くざっとと言う感じで、しっかりか?と突っ込まれると???なところがあります。が、オーバーライドとオーバーロードの違いは知っているつもりです。クラスを作ったりしたことはあるのと、ON_MESSAGEでメッセージを定義してそれを投げたり受けたりということはやったことがあります。が、オーバーロードを実際に自分のソースコードで書いた経験は今のところありません。

    以下、答えになっていないかもしれませんが、

    オーナードローをやるときに

    void MyButtonOwnerDraw::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)

    というようなような関数を定義して、コマンドボタンを書き換えるようなオーバーライドはやったことがあります。

    2010年11月3日 3:50
  • が、出てくる(つまり、引数は両方ともchar*でCSrtingやCSrting*が出てこない)のですが、CStringから、charへ変えたいのに、関数へ渡す第2引数がキャストもせずに直接

    strcpy_s(nstring, orig);
    と書いてあるのが感覚的に理解できていないのですが、

    「Unicode 文字セットでは CString の中身は wchar_t ベースになり、マルチバイト文字セットでは CString の中身は char ベースになります。」

    ということなのでしょうか?

    この辺は、C++言語についてもう少し突っ込んで勉強する必要があると思います。

    C++言語には演算子のオーバーロードと言うのがあるのはご存知ですか?
    型変換を行なうキャストもまた演算子として捉える事ができ、CStringクラスにはPCXSTRへのキャストが
    定義されています。PCXSTRはconst 文字列へのポインタです。この場合、使用する文字コードによって
    charのポインタになったり、wchar_tのポインタになったりすると思ってください。
    (Visual C++ 2005以降、CStringはテンプレートクラスとして定義されていて実装が階層的に
    行なわれています。キャスト演算子PCXSTRをMSDN上で探すにはその階層を辿る必要があります)
    この場合、このキャスト演算子が定義されているので暗黙的に型変換が行なわれます。
    但し、この暗黙と言うのはコンパイラが利用するべき演算子を一意に特定できる場合だけです。
    特定できない場合はエラーが出るので明示的にキャストする必要が出てきます。

    使用する文字コードの指定によってCStringが内部で扱う文字列のデータ型をcharにしたり、wchar_tにしたり
    する事とキャストせずに指定できる事の間には直接の関係は有りません。
    暗黙の型変換に使用できる演算子が存在しているかどうかが鍵になります。

    オーバーロードとオーバーライドに関してはC++言語の特徴的な部分になりますので
    どういうものであるのかと同時に使う上での注意点を理解しておく必要があります。
    ご本人がどのような方法でC++言語の文法を勉強されたのか分かりませんが、
    C++言語の入門書で一通りの内容を通して勉強されていないのでしたら
    今からでも通しで勉強される事をお勧めします。

    で、オーバーロード云々の話ですが、
    C++言語では関数の引数の型や個数の違う同名の関数を複数定義できることですよね。
    この事からエラーメッセージは、strcpy_sと言う関数名で多重定義された幾つかの関数の中に
    指定された引数の型と数の条件に合う関数はなかったというエラーになるわけです。
    平たく言うとそんな関数は存在しないって事ですね。
    HELPを見るとわかりますが、書かれているような使い方でstrcpy_sを使う場合、
    引数は3つ必要になります。従って該当する関数は見つからないとなるわけです。
    C言語ですとオーバーロードが無いので引数が間違っていますと出せますが、
    C++言語の場合、オーバーロードの機能があるのでそういうメッセージにならないわけです。

    ちなみにですが、VS2010の方であれば、引数3個の関数呼び出しを使っているようですよ。
    悲しいかな、MSDNのサンプルと言えど完璧と言うわけでは無いのでそういう場合は
    こういう場で質問してみるのはOKだと思います。
    その時に自分はこう思うのだけれど、まで提示できればベストですね。


    解決した時は、参考になったレスポンスの所にある[回答としてマーク]ボタンをクリックしてスレッドを締めましょう。
    • 回答としてマーク 山本春海 2010年11月29日 4:21
    2010年11月3日 4:38
  • PATIOさんありがとうございます。

    >C++言語には演算子のオーバーロードと言うのがあるのはご存知ですか?

     名前だけ知ってます。言い換えると、中身は詳しく知りませんでした。

    >型変換を行なうキャストもまた演算子として捉える事ができ、CStringクラスにはPCXSTRへのキャストが
    >定義されています。PCXSTRはconst 文字列へのポインタです。この場合、使用する文字コードによって
    >charのポインタになったり、wchar_tのポインタになったりすると思ってください。

    文字列の扱いという話からは少しそれるのですが、例えば、
    DrawItem( LPDRAWITEMSTRUCT lpDrawItemStruct)
    とか
    LRESULT MessageFunc(WPARAM valWPARAM, LPARAM valLPARAM);
    とか
    ReceiveThread(LPVOID arg)
    というのがあって、
    引数のところに、LPDRAWITEMSTRUCT、WPARAM、LPVOIDのような大文字で定義された型があります。上記に限らず、大文字で定義された型がそこらじゅうにたくさん出てきますが、これらは全部「演算子のオーバーロード」ですか?

    >使用する文字コードの指定によってCStringが内部で扱う文字列のデータ型をcharにしたり、wchar_tにしたり
    >する事とキャストせずに指定できる事の間には直接の関係は有りません。
    >暗黙の型変換に使用できる演算子が存在しているかどうかが鍵になります。

    そういうことなのですね。知りませんでした。

    >C++言語の入門書で一通りの内容を通して勉強されていないのでしたら
    >今からでも通しで勉強される事をお勧めします。

    いちおう、基礎的な本は持っています。ただ、演算子の扱い(オーバーロード)の頁は読み飛ばしていました。暫くの間は、そんなところを使うと思っていなかったので・・・こんど読みます。

    2010年11月3日 6:10
  • DrawItem( LPDRAWITEMSTRUCT lpDrawItemStruct)
    とか
    LRESULT MessageFunc(WPARAM valWPARAM, LPARAM valLPARAM);
    とか
    ReceiveThread(LPVOID arg)
    というのがあって、
    引数のところに、LPDRAWITEMSTRUCT、WPARAM、LPVOIDのような大文字で定義された型があります。上記に限らず、大文字で定義された型がそこらじゅうにたくさん出てきますが、これらは全部「演算子のオーバーロード」ですか?

    いいえ、単純に全て大文字だからと言うわけではないです。
    この辺の型はそもそもC言語の頃からあったものですし、オーバーロードとは関係有りません。
    この辺はデータ型を使いやすくしたり、状況によって切り替える事ができるようにする為に別名をつけている物が多いです。
    例えば、LPVOIDはvoid *の別名です。
    そういう意味で言うとPCXSTRもデータ型の名前です。
    実際にオーバーロードされた演算子を表現する場合は、キャスト演算子の場合、変換される型と同じ名前で作成されるので
    結果的に同じになっているだけです。全て大文字と言う事とキャスト演算子のオーバーロードとの関係はありません。
    そこは分けて考えてください。
    例えばですが、あるクラスCDataにint型にキャストする演算子を定義する事だってできます。
    その場合、int CData::operator int() const;と言う風に定義ができます。
    大文字である事が必須ではない事がわかると思います。

     

    文字列の扱いという話からは少しそれるのですが、例えば、

    >使用する文字コードの指定によってCStringが内部で扱う文字列のデータ型をcharにしたり、wchar_tにしたり
    >する事とキャストせずに指定できる事の間には直接の関係は有りません。
    >暗黙の型変換に使用できる演算子が存在しているかどうかが鍵になります。

    そういうことなのですね。知りませんでした。

    >C++言語の入門書で一通りの内容を通して勉強されていないのでしたら
    >今からでも通しで勉強される事をお勧めします。

    いちおう、基礎的な本は持っています。ただ、演算子の扱い(オーバーロード)の頁は読み飛ばしていました。暫くの間は、そんなところを使うと思っていなかったので・・・こんど読みます。

    実は、MFCを使いこなす為にはC++言語の文法と概念的な部分の理解が不可欠です。
    クラスの概念、派生、仮想関数、純粋仮想クラス、オーバーロード、オーバーライド等々。
    MFCはクラスライブラリなのでこういった知識が土台に無いと使いこなせませんし、
    どうして派生する必要があるのかとか、どのように派生クラス側で定義したら良いのか等の
    実装レベルの話についていけない状態になります。
    コンパイルエラー等に対処するにも文法の知識は必要です。
    一度に全てを理解するのは無理でも一通りは読んでおいてこの内容は確か書いてあったな
    くらいの状態にはしておいた方が良いですよ。
    単なる入門書ではなくてコンソールプログラムを書きながら実際に演習をするタイプの本が
    こういう勉強には向いています。オブジェクト指向の考え方の部分まである程度カバーできていると
    MFCを実際に使ってプログラミングする時でもなぜそういう実装になっているのかが
    ある程度わかるようになります。そうすれば、基本的な使い方から発展させて使う事が
    できるようになると思います。

     


    解決した時は、参考になったレスポンスの所にある[回答としてマーク]ボタンをクリックしてスレッドを締めましょう。
    • 回答としてマーク 山本春海 2010年11月29日 4:21
    2010年11月3日 6:37
  •  

     PATIOさん、ありがとうございます。

    HELPを見るとわかりますが、書かれているような使い方でstrcpy_sを使う場合、
    引数は3つ必要になります。従って該当する関数は見つからないとなるわけです。
    C言語ですとオーバーロードが無いので引数が間違っていますと出せますが、
    C++言語の場合、オーバーロードの機能があるのでそういうメッセージにならないわけです。

    HELPってどこですか?

    http://msdn.microsoft.com/ja-jp/library/td1esda9(VS.80).aspx

    を見る限り、 

    私がやろうとしていることと、「引数は3つ必要になります。」の関連性がという内容を読み取れないのですが、どういう意味ですか?実は、

     const CString orig("Hello, World!");
        const size_t newsize = 100;
        char nstring[newsize];
        strcpy_s(nstring,100, orig);
        strcat_s(nstring, " (char *)");

    とやって見たのですが、

    error C2664: 'strcpy_s' : 3 番目の引数を 'const CString' から 'const char *' に変換できません。

    というエラーで止まってしまいます。3番目の引数はどう定義したらよいのでしょうか?char で言う、char *Test = "Hello, World!" という定義は、CStringのときはどう書けばよいんでしょうか?

    どこかにサンプルソースがありませんか?文字の扱い方を除く他のものはたいがいネットで見つかるのに、なぜか文字列の扱いに関するものは皆無に等しいです(あるにはあるが、ビルドが通らなかったりちゃんと動かないものばかり)

    2010年11月3日 10:05
  • char で言う、char *Test = "Hello, World!" という定義は、CStringのときはどう書けばよいんでしょうか?

    何を目的にするか、どういったレベルで解決とするか次第でしょうか。

    案1:プロジェクトの設定を マルチバイト文字セット に変える。
    案2:CString を使わず、char 型ベースの CStringA を使う。
    案3:wchar_t 型ベースの関数 wcscpy_s に乗り換え、wchar_t 型ベースでコードを書く。
    案4:きちんと変換する。CT2A クラスを使うなど。

    この段階ではややこしくなるので、TCHAR の概念は省略します。
    また、それぞれの方法の詳細にはこの投稿の段階では踏み込みません。

    どこかにサンプルソースがありませんか?文字の扱い方を除く他のものはたいがいネットで見つかるのに、なぜか文字列の扱いに関するものは皆無に等しいです(あるにはあるが、ビルドが通らなかったりちゃんと動かないものばかり)

    ややこしいからこそ、サンプルが少ないのでしょう。
    ビルドが通らないのは、プロジェクトの設定が悪いからかもしれませんので、「ちゃんと動かないものばかり」と断じる前に検証しましょう。

    Visual C++ 2005 から新規に作ったプロジェクトは、wchar_t 型ベースの文字列を基本とします。
    CString が wchar_t 型ベースとなるためです、char 型ベースのサンプルと CString の組み合わせはうまく動作しません。
    文字セットの概念をとりあえず忘れて試したいだけであれば、最初にも書きましたが、プロジェクトの設定の文字セットを、Unicode 文字セットからマルチバイト文字セットに変えてください。


    質問スレッドで解決した場合は、解決の参考になった投稿に対して「回答としてマーク」のボタンを押すことで、同じ問題に遭遇した別のユーザが役立つ投稿を見つけやすくなります。
    • 回答としてマーク 山本春海 2010年11月29日 4:21
    2010年11月3日 13:47
    モデレータ
  • Azuleanさん、ありがとうございます。

    何を目的にするか、どういったレベルで解決とするか次第でしょうか。

    目的は、エディットボックスに記入されたCString型の文字列(ただし数字のみで構成された文字列)を、プログラム上でint型として扱いたいというような、単に文字列を数値へ変換したいだけです。その前段階として、CStringをcharに変えようと思いました。ですから、他に方法があるならそれでも良いと思っていますので、案2と案3を少し試してみようかと思っています。都合よのい表現があったので使わせていただきますが、「文字セットの概念をとりあえず忘れて試したいだけ」というレベルです。

     

    この段階ではややこしくなるので、TCHAR の概念は省略します。
    また、それぞれの方法の詳細にはこの投稿の段階では踏み込みません。

    そのほうが私も助かります。TCHAR もどこかのHPで見たことはあるのですが、ざっと読んでみて下手にかかわるのは嫌な予感がしたので読み飛ばしています。文字列操作は私の実験、検討材料にとっておまけ的な機能だということもあり、あまり深く関わらないほうがよさそうです。

     

    ビルドが通らないのは、プロジェクトの設定が悪いからかもしれませんので、「ちゃんと動かないものばかり」と断じる前に検証しましょう。Visual C++ 2005 から新規に作ったプロジェクトは、wchar_t 型ベースの文字列を基本とします。

    サンプルはどれも動作保証のない、あくまでサンプルなので動かなくても当たり前だと思っていますが、確かにサンプルプログラムにはプロジェクトの設定までは書いていませんね。そういえば、ネットで見つけたサンプルはほとんどが2005以前のものが多かったです。私は、visual studio 2008を使っていますが、2008向けのサンプルはもしかしたら一度も見たことが無いかもしれません。(記憶があいまいですが)

     

    文字セットの概念をとりあえず忘れて試したいだけであれば、最初にも書きましたが、プロジェクトの設定の文字セットを、Unicode 文字セットからマルチバイト文字セットに変えてください。

    「文字セットの概念をとりあえず忘れて試したい」というレベルなので、実は昼間に試してみました。しかし、別の場所で文字列定義をしているところが幾つもあり、こんどはそっちがビルドエラーで引っかかってきてしまうので、この方法で、(私にとって)臭いものに蓋をする野望は失敗に終わりました。ここでいう、「別の場所での文字列定義」は、普通のchar*配列でプログラム上でリストボックスやコンボボックスを表示するために使うような、文字列データが定義された配列です。(けっこう定義数が多い)

    2010年11月3日 15:13
  • 「文字セットの概念をとりあえず忘れて試したい」というレベルなので、実は昼間に試してみました。しかし、別の場所で文字列定義をしているところが幾つもあり、こんどはそっちがビルドエラーで引っかかってきてしまうので、この方法で、(私にとって)臭いものに蓋をする野望は失敗に終わりました。ここでいう、「別の場所での文字列定義」は、普通のchar*配列でプログラム上でリストボックスやコンボボックスを表示するために使うような、文字列データが定義された配列です。(けっこう定義数が多い)

    ビルドエラーになると言うことは、wchar_t 型ベースの文字列前提の処理がすでに存在していると言うことですね。
    char 型ベースの文字列で進めようとすると、今回エラーになった箇所とこれから作る箇所のつなぎ目で再び問題が発生するように思えます。
    当面の作戦としては、wchar_t 型ベースの文字列で統一していった方がいいかもしれません。

    (文字列定義の部分が気になりますが、話を簡単にするため一旦放置します)

    目的は、エディットボックスに記入されたCString型の文字列(ただし数字のみで構成された文字列)を、プログラム上でint型として扱いたいというような、単に文字列を数値へ変換したいだけです。その前段階として、CStringをcharに変えようと思いました。ですから、他に方法があるならそれでも良いと思っていますので、案2と案3を少し試してみようかと思っています。都合よのい表現があったので使わせていただきますが、「文字セットの概念をとりあえず忘れて試したいだけ」というレベルです。

    今の設定の GetWindowText で取得した場合、wchar_t 型ベースの CString になります。
    このため、wchar_t 型ベースで処理していった方が問題は少ないように思えます。
    char 型ベースの関数を使おうとすると、変換が必要になって、煩雑になると思われます。

    wchar_t 型ベースの関数名がわからない場合は、MSDN で調べましょう。
    たとえば、strcpy_s 関数の場合、次のページの下の方に wcscpy_s が出てきます。
    http://msdn.microsoft.com/ja-jp/library/td1esda9(VS.80).aspx

    (本当には tchar.h (_tcscpy_s ) を使った方が良いですが、今の問題が解消するまでは一旦置いておきます。解消したら、tchar.h だと何が良いのかとか調べてみてください)


    質問スレッドで解決した場合は、解決の参考になった投稿に対して「回答としてマーク」のボタンを押すことで、同じ問題に遭遇した別のユーザが役立つ投稿を見つけやすくなります。
    • 回答としてマーク 山本春海 2010年11月29日 4:21
    2010年11月3日 21:52
    モデレータ
  • 後回しにしてはと言う話になっていますけれど、一応書いておきますね。

    目的は、エディットボックスに記入されたCString型の文字列(ただし数字のみで構成された文字列)を、プログラム上でint型として扱いたいというような、単に文字列を数値へ変換したいだけです。その前段階として、CStringをcharに変えようと思いました。ですから、他に方法があるならそれでも良いと思っていますので、案2と案3を少し試してみようかと思っています。 都合よのい表現があったので使わせていただきますが、「文字セットの概念をとりあえず忘れて試したいだけ」というレベルです。
    そのほうが私も助かります。TCHAR もどこかのHPで見たことはあるのですが、ざっと読んでみて下手にかかわるのは嫌な予感がしたので読み飛ばしています。文字列操作は私の実験、検討材料にとっておまけ的な機能だということもあり、あまり深く関わらないほうがよさそうです。

    TCHARに関してはちゃんと理解しないと拙いような気がします。VC++6.0の頃からあった概念のはずですし。
    以前のプロジェクトでは、扱う文字コードの規定値がマルチバイトコードになっていて、最近ではUnicodeになっているのでTCHARを使わずにコードを書いてしまうとその部分の切替が自動で出来ないのでどちらかに合わせて書き直す必要が出てきます。
    この部分の切替をダイナミックに行う為のTCHARとTCHARを扱う関数群になるのでVisual C++を使うのであれば、おさえておかないとだめです。プロジェクトの設定の切替でダイナミックに切り替えてしまってかまわない部分の記述に関してはTCHAR系に統一してしまった方が良いですよ。MFCのクラスもプロジェクトの設定でダイナミックに切り替わるようになっているのでTCHARで書くようにしておけば、特に意識する必要はないです。
    リテラルの文字列に関してもTCHARの文字列として記述できますからそうしておけばプロジェクトの設定だけで切替が可能です。_T("ああ")と言う風に書くだけですし。一度分かってしまえば、そこまでややこしい話では無いと思いますよ。文字コードの話を深くしていくと話が長くなるのでこの場での説明は厳しいかもしれませんけれど。


    「文字セットの概念をとりあえず忘れて試したい」というレベルなので、実は昼間に試してみました。しかし、別の場所で文字列定義をしているところが幾つもあり、こんどはそっちがビルドエラーで引っかかってきてしまうので、この方法で、(私にとって)臭いものに蓋をする野望は失敗に終わりました。ここでいう、「別の場所での文字列定義」は、普通のchar*配列でプログラム上でリストボックスやコンボボックスを表示するために使うような、文字列データが定義された配列です。(けっこう定義数が多い)

    正にこういうことが起こらないようにする為のTCHARですから、TCHARで書き換えてしまってプロジェクトの設定の方で文字コードの切替をした方が良いと思いますよ。
    プロジェクトの文字コードの設定に関わらず固定にしたい場合だとTCHARを使わずに直接指定することになりますけれど、その場合はプロジェクトの文字コードの設定に合わせてコード変換する必要が出てくるケースもあると思います。
    文字コードの問題はどちらにしてもついてまわる話なので、私は放置しないで確認した方が良いと思います。
    むしろ、今回の事で再確認ができてよかったと思いますよ。


    解決した時は、参考になったレスポンスの所にある[回答としてマーク]ボタンをクリックしてスレッドを締めましょう。
    • 回答としてマーク 山本春海 2010年11月29日 4:22
    2010年11月4日 0:28
  • 1.文字セットを意識しないで、
    2.サンプルコードをコンパイルしたい。

    ということですが、古くからの多くのサンプルは概ねMBCSを前提に書かれています。
    一方、C++ネイティブコンパイラのデフォルトの設定はUnicodeです。
    なので、ソースを一切変更せずにコンパイルを通したいのなら。
    プロジェクトのプロパティで、「マルチバイト文字セットを使用する」
    に設定しなおすしかありません。初心者にはこの方がわかりやすいかもしれませんね。
    Unicode化はその後でも仕方ないかもしれません。

    さて、表面上はオプションの変更にすぎませんが、Unicodeに設定するというのは、
    文字コードが変わるという以上に劇的な変更となります。
    個人的には、よくもTCHARで吸収できてるもんだねぇと感心するほどです。
    また、Unicode化しても、「ファイルにASCIIテキストで出力」などの要望が
    でた瞬間にMBCSも意識しなければなりません。この辺は慣れが必要だと考えます。
    とりあえず。
    http://msdn.microsoft.com/ja-jp/library/06b9yaeb(v=VS.90).aspx
    あたりを一読して心にとめておいて、練習はMBCSでやるというのを
    自分はお勧めします。

     

    • 回答としてマーク 山本春海 2010年11月29日 4:21
    2010年11月4日 9:38
  • Azuleanさん、ありがとうございます。

    >この段階ではややこしくなるので、TCHAR の概念

    >tchar.h だと何が良いのか
    という話には目をつむり、とりあえず手を動かして(=頭を使わずに)ソースコード上にあるcharという文字列をTCHARへ置換して、tchar.hのインクルード定義をしてビルドしてみました。すると、とりあえずビルドが通りました。

     int StringLength = mCEdit_PosX.GetWindowTextLengthW() + 1;
     static LPTSTR lptstr_PosX = new TCHAR[StringLength];
     CString CStr_PosX;
     mCEdit_PosX.GetWindowTextW(CStr_PosX);
     _tcscpy_s(lptstr_PosX, StringLength, CStr_PosX);
    と目的のchar*と離れてしまったものの、一応、ビルドは通るようになりました。LPTSTRというよくわからない型が出てきたことも事実ですが、今回は結果オーライです。

    --------------------------------------------------------------------------------------------------------

    >(文字列定義の部分が気になりますが、話を簡単にするため一旦放置します)

    文字列置換前のイメージは、
    char *Eto[] = { "子", "牛", "寅", "兎", "辰", "巳", "馬", "羊", "申", "酉", "戌", "亥", NULL}

    で、置換後のイメージは

    const TCHAR *Eto[] = { _T("子"), _T("牛"), _T("寅"), _T("兎"), _T("辰"), _T("巳"), _T("馬"), _T("羊"), _T("申"), _T("酉"), _T("戌"), _T("亥"), NULL};


    ですが、どこか変なところはありますか?

    2010年11月5日 3:27
  • PATIOさん、ありがとうございます。

    >正にこういうことが起こらないようにする為のTCHARですから、TCHARで書き換えてしまってプロジェクトの
    >設定の方で文字コードの切替をした方が良いと思いますよ。

    今回は、文字列の置換だけで済んだので結果オーライです。


    以下、今の私にとっては余談ですが、、、、
    試しにプロジェクトの設定をマルチバイトに変えてビルドしてみたら、それはそれでエラーが出ますので単にTCHARだけが解決すれば良いというものではなかったです。
     LoadBitmapWなどのW付き関数をたくさん使っていました。Wが確か、なんかの文字コード依存を意味する記号だと思います。これに関連しては、
     「'LoadImageW' : 2 番目の引数を 'LPSTR' から 'LPCWSTR' に変換できません。(新しい機能 ; ヘルプを参照)」
    というのも出ています。が、今回はユニコードでビルドが通ったのでこれでいいです。(そのうち必要が出てきたら、考えます)

    2010年11月5日 3:28
  • 中澤さん
    情報、有難うございました。

    昨日、目は通してみたのですが、ほとんど理解できていません。お話のとおり、「心に留めておく」程度にしておこうと思います。

    とりあえず、運良くTCHAR型にしてビルドエラーは取れました。理解はしていないので、「解決」とは違いますが暫くは深く触れないようにしようと思います。

    2010年11月5日 3:30
  • 試しにプロジェクトの設定をマルチバイトに変えてビルドしてみたら、それはそれでエラーが出ますので単にTCHARだけが解決すれば良いというものではなかったです。

    LoadBitmapWなどのW付き関数をたくさん使っていました。Wが確か、なんかの文字コード依存を意味する記号だと思います。これに関連しては、
    「'LoadImageW' : 2 番目の引数を 'LPSTR' から 'LPCWSTR' に変換できません。(新しい機能 ; ヘルプを参照)」
    というのも出ています。が、今回はユニコードでビルドが通ったのでこれでいいです。(そのうち必要が出てきたら、考えます)

    えーと、ウィザードで出力した雛形に入ってましたか?
    人の手で追加されている部分の話をしているのであれば、それはそのコードを書いた人の責任です。
    ちゃんとLPCTSTRを引数にしたLoadBitmapやLoadImage関数は存在しているのでそっちを使えば、
    本当にダイナミックに切替が可能です。

    これはあくまでもプログラム内で扱う文字コードをどちらかに統一できる場合の話なので
    万能と言うわけでは有りませんが、少なくともプロジェクトの設定の部分で切り替える場合には
    対応ができます。
    ポイントは文字列を扱う関数に関してはTCHARを扱う為の物を使用する。
    リテラル文字列はTCHARの文字列として定義する。
    くらいです。MFCは最初からプロジェクトの設定でダイナミックに切り替わるように出来ています。
    インテリセンスがそこまで考えてくれていないケースがあるのでインテリセンスで出てきた関数名を
    そのまま使ってしまうと言われているようなケースが起こりえます。
    例えば、Unicode指定時にLoadBitmapをインテリセンスで出そうとするとLoadBitmapWが
    出てきたりと言う話。間違ってはいないけれど、TCHARを扱う関数で書いておいた方が切替が
    簡単にできるので良いと思います。
    状況によってはあえてマルチバイトコード用やUnicode用を意図的に使う場合もありますけれど、
    これはプログラマが意図して使っているわけなので当然意味が分かって使っているはずです。
    逆に意味がわかっていないならちゃんと勉強しましょうねと言う話なのですが。

     


    解決した時は、参考になったレスポンスの所にある[回答としてマーク]ボタンをクリックしてスレッドを締めましょう。
    • 回答としてマーク 山本春海 2010年11月29日 4:21
    2010年11月5日 4:23
  • PATIOさんありがとうございます。

    えーと、ウィザードで出力した雛形に入ってましたか?
    人の手で追加されている部分の話をしているのであれば、それはそのコードを書いた人の責任です。
    ちゃんとLPCTSTRを引数にしたLoadBitmapやLoadImage関数は存在しているのでそっちを使えば、
    本当にダイナミックに切替が可能です。  そういうことですね。

    ただ、インターネットから引っ張ってきたサンプルコードの中に含まれていたので、「人の手」です。また、

    winUser.h(8590):#define LoadBitmap  LoadBitmapW

    という定義がされているのですが、ソースコード上でW付きと無しを書き分けて、意味があるのでしょうか?どっちも同じ結果になりそうです。

    また、最初は、私もLoadBitmap を使っていたのですが、いつだったかビルドの時にワーニングが出て、「その関数はセキュリティが弱いから、W付きのほうを使え」という内容だったのですが、これは無視して良いのでしょうか?

     

    これはあくまでもプログラム内で扱う文字コードをどちらかに統一できる場合の話なので
    万能と言うわけでは有りませんが、少なくともプロジェクトの設定の部分で切り替える場合には
    対応ができます。

    そういう意味だったのですね。知りませんでした。 ありがとうございます。

    2010年11月5日 5:20
  • winUser.h(8590):#define LoadBitmap  LoadBitmapW

    という定義がされているのですが、ソースコード上でW付きと無しを書き分けて、意味があるのでしょうか?どっちも同じ結果になりそうです。

    また、最初は、私もLoadBitmap を使っていたのですが、いつだったかビルドの時にワーニングが出て、「その関数はセキュリティが弱いから、W付きのほうを使え」という内容だったのですが、これは無視して良いのでしょうか?

    その行だけ見ても本当の意味がわからないと思いますよ。
    その行の前後まで含めてみれば、プロジェクトの設定で切り替えられるからくりもわかるはずです。
    WとかAがついてるものはそれぞれがUnicode用とマルチバイトコード用です。
    UNICODEという定義が有るかで切り替えられるようになっています。

    つまり、LoadBitmapで書いておけば、その定義でダイナミックに切り替わると言う事です。
    他のAPIに関しても文字列を扱うような物に関しては同じような定義になっていると思います。

    私の環境(VisualStudio2008)ではワーニングは出ませんけれど、どういう記述をした時に出たんでしょう?
    通常、セキュリティ系のワーニングが出るのはstr系のランタイム関数で_s付きの関数を使えという時に
    出ていたと思うのですけれど。

     


    解決した時は、参考になったレスポンスの所にある[回答としてマーク]ボタンをクリックしてスレッドを締めましょう。
    • 回答としてマーク 山本春海 2010年11月29日 4:21
    2010年11月5日 7:53
  • ソースコード上でW付きと無しを書き分けて、意味があるのでしょうか?どっちも同じ結果になりそうです。

    マルチバイト文字セットを使っているときにあえて、Unicode 文字セットの関数を呼び出したいとか、TCHAR 思想の #define がない API から受け取った文字列を渡すときとかでしょうか。
    文字列を扱う上で明確な目的があれば、W をつけることはあり得ます。
    結果としては同じになるかもしれませんし、プロジェクトの設定によっては違うかもしれません。

    ただ、そのソースコードを誰かと共有する、プロジェクトの設定を変えることがあるといった事情がない限り、TCHAR を深く考えなくて良いとは思います。

    また、最初は、私もLoadBitmap を使っていたのですが、いつだったかビルドの時にワーニングが出て、「その関数はセキュリティが弱いから、W付きのほうを使え」という内容だったのですが、これは無視して良いのでしょうか?

    見た覚えがありません。
    もう一度、同じようなコードを書くなど、再確認してみてください。


    質問スレッドで解決した場合は、解決の参考になった投稿に対して「回答としてマーク」のボタンを押すことで、同じ問題に遭遇した別のユーザが役立つ投稿を見つけやすくなります。
    • 回答としてマーク 山本春海 2010年11月29日 4:21
    2010年11月5日 13:44
    モデレータ
  • PATIOさん、ありがとうございます。

    その行だけ見ても本当の意味がわからないと思いますよ。
    その行の前後まで含めてみれば、プロジェクトの設定で切り替えられるからくりもわかるはずです。
    WとかAがついてるものはそれぞれがUnicode用とマルチバイトコード用です。
    UNICODEという定義が有るかで切り替えられるようになっています。

    失礼しました。文字列、LoadBitmapを検索した検索結果の出力窓を見ていた(ファイル実体を見ていなかった)ので、その1行しか見ていませんでした。

    つまり、LoadBitmapで書いておけば、その定義でダイナミックに切り替わると言う事です。
    他のAPIに関しても文字列を扱うような物に関しては同じような定義になっていると思います。

    私の環境(VisualStudio2008)ではワーニングは出ませんけれど、どういう記述をした時に出たんでしょう?
    通常、セキュリティ系のワーニングが出るのはstr系のランタイム関数で_s付きの関数を使えという時に
    出ていたと思うのですけれど。

    普通にLoadBitmap関数を定義しただけだったと記憶していたので、いまソースを書いてみたのですが、 再現しなくなってしまいました。記憶違いか、当時読み間違えたかだと思います。

    2010年11月6日 14:03
  • Azuleanさん、ありがとうございます。

    ソースコード上でW付きと無しを書き分けて、意味があるのでしょうか?どっちも同じ結果になりそうです。

    マルチバイト文字セットを使っているときにあえて、Unicode 文字セットの関数を呼び出したいとか、TCHAR 思想の #define がない API から受け取った文字列を渡すときとかでしょうか。

    そういう使い方をするものなのですね。

    また、最初は、私もLoadBitmap を使っていたのですが、いつだったかビルドの時にワーニングが出て、「その関数はセキュリティが弱いから、W付きのほうを使え」という内容だったのですが、これは無視して良いのでしょうか?

    見た覚えがありません。
    もう一度、同じようなコードを書くなど、再確認してみてください

    済みません。勘違いだったようです。再現しなくなってしまいました。
    2010年11月6日 14:08