SQLサーバ処理でエラー時の処理について

[戻る]
田中さん 2006/07/24(月) 18:41:27
開発環境VisualC++、SDK開発 Windows2003Server SQLServer2000

SQLサーバーに値を入力するプログラムを作っています。
そこで質問なのですが、Try処理でデータを入力しようとして、制約違反でエラーとなっ
た場合の処理について質問です。

データを登録するコードは以下のようにしています。
int SqlCds::Test(){
HRESULT    hr = S_OK;

//ADOオブジェクト
_ConnectionPtr  pConnection = NULL;    //コネクション作成
_CommandPtr     pCmdChange  = NULL;    //コマンド作成

try
{
//DB接続
_bstr_t strCnn(STR_CNN);//接続文字
TESTHR(pConnection.CreateInstance(__uuidof(Connection)));//インスタンス作成
pConnection->Open (strCnn, "", "", adConnectUnspecified);//コネクションOPEN
pConnection->Errors->Clear();    //DB接続のエラーをクリアする

//コマンドの実行
_bstr_t strSQLChange("INSERT INTO TEST (日付) VALUES(20060723)");//コマンド文字
TESTHR(pCmdChange.CreateInstance(__uuidof(Command)));    //コマンド作成
pCmdChange->ActiveConnection = pConnection;    //コマンド
pCmdChange->CommandText = strSQLChange;    //コマンドテキストを関連付け

pCmdChange->Execute(NULL,NULL,adExecuteNoRecords);//返事を返さなくても良い処理
    
//クローズ
pCmdChange->Release();
pConnection->Close();
}
catch (_com_error &e)
{
//エラー処理
PrintProviderError(pConnection);
PrintComError(e);
}
return TRUE;
}

この処理でデータは正常に登録できます。ただ、わざと制約違反のデータを登録した場合
にエラーが発生し、エラーメッセージを吐き出させるまではOKですが、その後、
ハンドルされていない例外が発生し、止まります。
エラーがおきる場所はcomutil.hの以下の場所です。

inline unsigned long _bstr_t::Data_t::Release() throw()
{
//ここの処理で止まる
if (!InterlockedDecrement(reinterpret_cast<long*>(&m_RefCount))) {
    delete this;
    return 0;
}
return m_RefCount;
}


エラー処理は以下のコードです。

///////////////////////////////////////////////
//      ProviderError を表示する関数  
///////////////////////////////////////////////
void SqlCds::PrintProviderError(_ConnectionPtr pConnection)
{

char emsg[256];    //エラー文字
ErrorPtr  pErr = NULL;

//初期化
memset(&emsg,0,sizeof(emsg));

if( (pConnection->Errors->Count) > 0)
{
long nCount = pConnection->Errors->Count;
// Collection ranges from 0 to nCount -1.
for(long i = 0; i < nCount; i++)
{
pErr = pConnection->Errors->GetItem(i);
wsprintf(emsg,"<ProviderError> \n number[%x]:%s", pErr->Number,pErr-
>Description);
OutputDebugStr(emsg);
//MessageBox(NULL,emsg,"SQL ProviderError",MB_OK);
}
}
}

//////////////////////////////////////
//      ComError を表示する関数
//////////////////////////////////////

void SqlCds::PrintComError(_com_error &e)
{
_bstr_t bstrSource(e.Source());
_bstr_t bstrDescription(e.Description());

//エラーメッセージの作成
char emsg[256];
memset(&emsg,0,sizeof(emsg));

wsprintf(emsg,"<ComError>:\n\t Code=%081x\n\t Code meaning=%s\n\t Source=%
s\n\t Description=%s\n",e.Error(),(char*) e.ErrorMessage(),(char*) e.Source(),
(char*) e.Description());
//MessageBox(NULL,emsg,"SQLエラー",MB_OK);
OutputDebugStr(emsg);
}


MSDNを参考にコードを作ったつもりなのですが、エラー処理でマズイ所どのあたりでしょ
うか?

2006/07/24(月) 19:08:18
本題とは関係ないけれど

>_bstr_t bstrSource(e.Source());
>_bstr_t bstrDescription(e.Description());
使ってないような…

>"<ComError>:\n\t Code=%081x\n\t Code meaning=%s\n\t Source=%s\n\t 
Description=%s\n"
これ固定文字列部分だけでも144バイトあるけどバッファ足りる?

2006/07/24(月) 19:10:05
>>"<ComError>:\n\t Code=%081x\n\t Code meaning=%s\n\t Source=%s\n\t 
>Description=%s\n"
>これ固定文字列部分だけでも144バイトあるけどバッファ足りる?
Code=%081x
は
Code=%08x
のタイプミス?

ITO 2006/07/24(月) 19:47:18
> char emsg[256];
サイズが小さいですね。
ODBC-SDKで言うサーバからのエラーメッセージを保存するなら、
500以上はあってもいいはず。
文字列(ソース上だと、「e.ErrorMessage()」かな?)だけで256バイト近くいく可能性
があります。(英文だと分らない)
MFCの関数だと、エラー処理は「Try-catch」になるんですね。
エラー処理には、入るみたいなんで、「Try-catch」はうまくいっているのですね。
使ったことがないんで分りませんが、「_com_error」の使い方はあっていますか?

ITO 2006/07/24(月) 20:03:16
「OutputDebugStr」→「OutputDebugString」ですか?

田中さん 2006/07/25(火) 04:52:08
ご返事ありがとうございます。
結論はエラーメッセージのBUFF不足でした。BUFFを500にすればエラーは出さないように
なりました。

>>_bstr_t bstrSource(e.Source());
>>_bstr_t bstrDescription(e.Description());
>使ってないような…
確かにコメントアウトしても問題なく動作しました。必要なかったのかな・・・?

>Code=%081xはCode=%08xのタイプミス?
その通りでした。修正しました。

>「OutputDebugStr」→「OutputDebugString」ですか?
「OutputDebugString」です。
OutputDebugStrはどこかでマクロが定義されているようなので、これでも動作するようで
す。

ただ、もうひとつ、上記のProviderError を表示する関数でエラーメッセージが文字化け
します。

関数は以下のように修正しました。

///////////////////////////////////////////////
//      ProviderError を表示する関数  
///////////////////////////////////////////////
void SqlCds::PrintProviderError(_ConnectionPtr pConnection)
{
char emsg[4000];    //エラー文字。念のため4000に設定
ErrorPtr  pErr = NULL;

//初期化
memset(&emsg,0,sizeof(emsg));

if( (pConnection->Errors->Count) > 0)
{
long nCount = pConnection->Errors->Count;
for(long i = 0; i < nCount; i++)
{
pErr = pConnection->Errors->GetItem(i);
wsprintf(emsg,"<ProviderError> \n number[%x]:%s\r\n", 
  pErr->Number,pErr->Description);
OutputDebugStr(emsg);
//MessageBox(NULL,emsg,"SQL ProviderError",MB_OK);
}
}
}

//関数はここまで。

出力されたエラーメッセージは以下のようになります
<ProviderError> 
 number[80040e2f]:フソ
<ProviderError> 
 number[80040e2f]:ト


ちなみにComErrorを表示する関数は問題なく以下のメッセージを吐き出します。
<ComError>:
     Code=80040e2f
     Code meaning=IDispatch error #3119
     Source=Microsoft OLE DB Provider for SQL Server
     Description=PRIMARY KEY 違反、制約 'PK_TEST': オブジェクト 'TEST' に
は重複したキーは挿入できません。

Blue 2006/07/25(火) 08:47:07
もしかして、pErr->Description の戻り値が _bstr_t ではないでしょうか?
その場合、wsprintf関数に渡すときにキャストをする(operator const char*を呼ぶ)必
要がありそう。

MSDNのサンプルでは LPCTSTR でキャストしています。
http://msdn.microsoft.com/library/ja/default.asp?
url=/library/ja/jpado260/htm/mdproconnectionstringxvc.asp
>             printf("Error number: %x\t%s\n", pErr->Number,
>                (LPCSTR)pErr->Description);

田中さん 2006/07/26(水) 03:28:12
[[解決]]
Blueさんありがとうございました。
まさしく原因はそれでした。

wsprintf(emsg,"<ProviderError> \n number[%x]:%s\r\n", 
  pErr->Number,(LPCSTR)pErr->Description);

とする事で解決しました。

古いスレッドには追加発言できません。