開発環境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を参考にコードを作ったつもりなのですが、エラー処理でマズイ所どのあたりでしょ
うか?
本題とは関係ないけれど
>_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バイトあるけどバッファ足りる?
>><ComError>:\n\t Code=%081x\n\t Code meaning=%s\n\t Source=%s\n\t
>Description=%s\n
>これ固定文字列部分だけでも144バイトあるけどバッファ足りる?
Code=%081x
は
Code=%08x
のタイプミス?
> char emsg[256];
サイズが小さいですね。
ODBC-SDKで言うサーバからのエラーメッセージを保存するなら、
500以上はあってもいいはず。
文字列(ソース上だと、「e.ErrorMessage()」かな?)だけで256バイト近くいく可能性
があります。(英文だと分らない)
MFCの関数だと、エラー処理は「Try-catch」になるんですね。
エラー処理には、入るみたいなんで、「Try-catch」はうまくいっているのですね。
使ったことがないんで分りませんが、「_com_error」の使い方はあっていますか?
「OutputDebugStr」→「OutputDebugString」ですか?
ご返事ありがとうございます。
結論はエラーメッセージの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' に
は重複したキーは挿入できません。
もしかして、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);
Blueさんありがとうございました。
まさしく原因はそれでした。
wsprintf(emsg,<ProviderError> \n number[%x]:%s\r\n,
pErr->Number,(LPCSTR)pErr->Description);
とする事で解決しました。