プロが教える店舗&オフィスのセキュリティ対策術

新たに作ったLPWSTRの内容を、あらかじめ用意しておいたLPWSTRにコピーしたいのですが、うまくいきません。
どうするのが正解なのでしょうか。

簡易化したソースコードと失敗例を載せておきます。
(見やすくするために行頭に全角スペースを入れています。)

class LPWSTR_Copy{
public:
  LPWSTR lpwstr;
public:
  // lpwstrにNewStrをコピーしたい
  void StrCopy( LPWSTR NewStr){
    // lpwstr= NewStr; // 失敗
    // lstrcpy( lpwstr, NewStr); // 失敗
    /* TCHAR tchar[20];
      lstrcpy( tchar, NewStr);
      lpwstr= tchar;
    */ // 失敗
    /* lpwstr= new TCHAR[sizeof(NewStr)];
      lstrcpy( lpwstr, NewStr);
    */ // 失敗(実行時エラー)
  }
};

void copy(LPWSTR_Copy *str){
  LPWSTR newstr= TEXT("コピー元文字列");
  str->StrCopy(newstr);
}
void main(){
  LPWSTR_Copy strcpy;
  copy(&strcpy);
  LPWSTR str= strcpy.lpwstr;
  // ↑ここでstr.lpwstrの中身が"コピー元文字列"となっていてほしい
}

A 回答 (4件)

お疲れ様です。



まず、お伺いしたいのが、「lpwstr= NewStr; 」で失敗していると言う事ですが、どう失敗しているのでしょうか?
理論上はこれでうまく行くはずです。
([Visual C++ Express Edition] + [Windows Visuta])でコンパイルしデバックモードで追っかけて問題が無い事も確認済み。
(copy関数内のnewstrはデータ領域に生成された文字列「コピー元文字列」の先頭アドレスを指しています。)
(LPWSTR_Copy::StrCopy関数は引数でもらってきた、スタック領域の先頭アドレスをきちんとlpwstrにコピーしています。)

ちなみに、こんなクラスの作り方はまずしません。
「静的領域・データ領域・スタック領域・ヒープ領域」等のメモリ区分についてご理解はありますか?

この回答への補足

回答ありがとうございます。

「lpwstr= NewStr;」について補足します。
void copy(LPWSTR_Copy *str)を抜けたとき、copy()の内部で作られたnewstrは消滅します。
するとnewstrのアドレスをコピーしたlpwstrも消えてしまう、ということです。

このクラスを作った理由は、vector<LPWSTR>で失敗したからです。
メモリ区分についてはよく知りません。ざっと調べた限りでは、
・プログラム領域:関数
・静的領域:グローバル変数、static変数用
・スタック領域:自動変数などの自動的に消える変数用
・ヒープ領域:ポインタにnewで割り当てた領域
・データ領域:?
という感じでしょうか。

下により原型に近いソースを書いておきます。

class LPWSTR_Copy{
public:
LPWSTR lpwstr;
public:
// lpwstrにNewStrをコピーしたい
void StrCopy( LPWSTR NewStr){
// どうにかこうにか
}
LPWSTR_Copy( LPWSTR lpw){
StrCopy(lpw);
}
/* そのほかの関数
void operator=( LPWSTR lpw);
operator LPWSTR();
*/
};

class UseLSC{
public:
vector<LPWSTR_Copy> vlc;
void In(){
WCHAR wc[20];
for( int i=0; i<20; i++){
wsprintf( wc, "a_%02d_", i);
vlc.push_back(wc);
}
return; // この地点でvlcの中身は全て"a_19_"。連番になってほしい。
}
void Make(){
In();
return; // この地点でvlcの中身は全て""。
}
}

void main(){
UseLSC use;
use.Make();
}



ちなみに、for文ではなく

vlc.resize(20);
vlc[0]= TEXT("a_00_");
vlc[1]= TEXT("a_01_");
vlc[2]= TEXT("a_02_");
...

と書けば一応うまくいきます。
しかし、総数が増えた場合を考えるとこれは避けたいのです。

補足日時:2009/09/20 22:03
    • good
    • 0

えーと


こんなクラスの作り方しないのは#1の方と同意です。笑

他の視点から気付いたのでそれを挙げますね。


LPWSTRを使っているならそれに統一すべきです。
なぜTCHARやTEXTを使ってるのでしょうか?

_T、TEXT、TCHARマクロは
UNICODEを定義するかしないかでCHAR、TCHARまたはLPSTRとLPWSTRを切り替えてくれるマクロです。

LPWSTRにするのであれば
L"文字列"
とLをつけるだけです。

Program Tips - UNICODE
http://www.246.ne.jp/~y-ookubo/program/tips/unic …
    • good
    • 0
この回答へのお礼

>こんなクラスの作り方
「vector<LPWSTR> vlc;」としたときと全く同じ挙動。つまり無意味ということ…。
たしか「クラス化してコピーコンストラクタ内でnewすれば新しい領域が取られるから、コピーしてからコピー元が消えても大丈夫かもしれない…」と思って作ったんです。
この方法はコピーはうまくいったんですが、その後にエラーが発生しました。

>TEXTマクロ
調べました。
TEXTマクロはLPWSTRではなくLPTSTRじゃないと意味なさそうですね。
WCAHRとTCHARの違いが分かってませんでした。勉強になります。

お礼日時:2009/09/20 23:51

まず、クラスの作り方で違うという意味については


C++で追加されたクラスという物は、データとそのデータを扱う関数をセットにしたものです。
よって、文字列をコピーするだけのクラスというものは意味がありません。

ということで、とりあえず動くものを添付します。

#include <windows.h>
#include <iostream>
#include <vector>

class cstring{
private:
LPWSTR lpwstr;
public:
cstring(){
lpwstr = NULL;
}
~cstring(){
lpwstr = NULL;
}
cstring( LPWSTR NewStr){
lpwstr = NULL;
lpwstr= new TCHAR[wcslen(NewStr)+1];
lstrcpy( lpwstr, NewStr);
}
const LPWSTR chr(){
return lpwstr;
}
void operator=( LPWSTR NewStr){
if (lpwstr != NULL) {
delete []lpwstr;
}
lpwstr= new TCHAR[wcslen(NewStr)+1];
lstrcpy( lpwstr, NewStr);
}
};

class UseLSC{
public:
std::vector<cstring> vlc;
void In(){
WCHAR wc[20];
for( int i=0; i<20; i++){
wsprintf( wc, L"a_%02d_", i);
vlc.push_back(wc);
}
}
void Make(){
In();
}
};

int main(){
int i;
setlocale( LC_ALL, "Japanese" );

UseLSC use;
use.Make();

for ( i=0; i<20; i++ ){
wprintf(L"%s\n", use.vlc[i].chr());
}

}

私も汎用機の世界でしか仕事をしていないので、TCHAR等のマクロやUnicodeプログラミングの世界については分からないので、少し不安な箇所もあります。。。
    • good
    • 0
この回答へのお礼

出来ました!
コピーコンストラクタでnewを使い割り当てるのが正解のようです。

最初に実行時エラーになったのは、
 lpwstr= new TCHAR[lstrlen(NewStr)+1];
とすべきところを
 lpwstr= new TCHAR[sizeof(NewStr)];
としていたからのようです。
http://www.pro.or.jp/~fuji/mybooks/cdiag/cdiag.7 …
などを見ると結果同じだと思ったのですが。

なにはともあれ解決しました。
ありがとうございました!

お礼日時:2009/09/21 01:52

>>「lpwstr= NewStr;」について補足します。


>>void copy(LPWSTR_Copy *str)を抜けたとき、copy()の内部で
>>作られたnewstrは消滅します。
>>するとnewstrのアドレスをコピーしたlpwstrも消えてしまう、
>>ということです。

>>このクラスを作った理由は、vector<LPWSTR>で失敗したから
>>です。
>>メモリ区分についてはよく知りません。ざっと調べた限りでは、
>>・プログラム領域:関数
>>・静的領域:グローバル変数、static変数用
>>・スタック領域:自動変数などの自動的に消える変数用
>>・ヒープ領域:ポインタにnewで割り当てた領域
>>・データ領域:?
>>という感じでしょうか。


補足します。

「データ領域」という言葉で正しかったかどうか調べてみましたが、ちょっと分かりませんでした。(申し訳ありません)
調べてみる限りでは「プログラム領域」で正しいような感じです。

これを踏まえて最初にご提示頂いたソースコートではcopy関数で記述されている「TEXT("コピー元文字列")」の文字列の実体は書換え不可能なプログラム領域に置かれます。
([Visual C++]+windowsの場合)
「newstrは消滅します」と記載されている内容は正しいのですが、消滅するまえに、lpwstrへ文字列の先頭アドレスをコピー出来ているため問題ありません。

以上。
    • good
    • 0
この回答へのお礼

NewStrが消えても中の文字列はメモリに残留し、そのアドレスを保持するlpwstrから参照できる…と思うのですが、どうもうまく出来ません。
全要素が同じ内容になるのは同じアドレスを使っているからまあいいとして、消滅後に参照できない問題は解決していません。

LPWSTR newstr= TEXT("コピー元文字列")
はスタック領域に取られるのでは?
そして他の自動変数などが領域を取る際、かつてのnewstrと同じ~+xバイトのアドレス位置に取ると、そこにあった文字列は破壊されます。
するとlpwstrからアクセスできるなら"コピxxx..."みたいになってしまうため、これを防ぐためにアクセス禁止にしているのだと思います。

お礼日時:2009/09/21 02:05

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!