C++ ADO 命令对象调用 MySql 的二进制类型的输出参数的存储过程时,用户必须预先提供长度信息吗?

zjs100901 2013-04-09 04:08:25
我写一个与数据库相关的 C++ 库,目的是在用 ADO 的命令对象调用 MySql 的一个存储过程。这个存储过程有输出参数,类型为 VARBINARY。在调用存储过程之前自然要设置参数,参数的长度需要由上帝(用户)指定,上帝不爽,还拿出证据(记录集的方式不要用户指定一个长度,多方便!)。

C++ 代码见 1 楼,MySql 表及存储过程见 2 楼,备注见 3 楼。

说说我的 GetProcedure_BinaryValue 是怎么来的:我是先在网上找到了很多
很多很多很多用记录集的方式的代码,就是 GetRecordSet_BinaryValue 的样子,我复制一份,将前两行
[code=C/C++]FieldPtr fpr = m_AdoRecordSet->GetFields()->GetItem(name);
long length = fpr->ActualSize;[/Code]改为:[code=C/C++]_ParameterPtr ppr = m_CommandPtr->Parameters->GetItem(_bstr_t(name));
long ExpectMaxLen = ppr->GetSize();[/Code]
把中间部分的 len 改为 ActualLen,感觉可读性好一些。然后加上了我的异常判断,嗯,就这样,大家说说有错吗?
记录集的方式的函数里面,length 和 len 的值是相同的。但是命令对象的方式,ppr->GetSize() 返回的是在添加参数时的那个长度,stream->GetSize() 返回的是字段实际的有效内容的长度。为什么呢?
GetProcedure_BinaryValue 这个函数的接口不变,你会怎么写函数体?


=============== When: 2013-04-09 Where: bbs.csdn.net Who: zjs100901 Which: 0008 ===============
==================== 以下为 CSDN 广告,与楼主发贴内容无关,如有瓜葛,纯属巧合 ====================
...全文
301 9 打赏 收藏 转发到动态 举报
写回复
用AI写文章
9 条回复
切换为时间正序
请发表友善的回复…
发表回复
zjs100901 2014-06-01
  • 打赏
  • 举报
回复
那就这样子好了
zjs100901 2014-05-31
  • 打赏
  • 举报
回复
忘了结贴了
巫师 2013-10-25
  • 打赏
  • 举报
回复
老大很NB啊,马甲都两个花花了。。。
zjs100901 2013-04-17
  • 打赏
  • 举报
回复
感谢上面两位老师的顶帖。
孔方兄_ 2013-04-10
  • 打赏
  • 举报
回复
引用 3 楼 zjs100901 的回复:
命令对象、存储过程、输出参数、二进制,四个条件缺一不可。 用户觉得他没有责任为该二进制提供一个 ExpectMaxLen,用户觉得他的理想就是只给出一个空的 string,要我往里填。毕竟用户是 C++ 程序员,一个 string 引用作输出参数,怎么会要先说出长度来呢? 各位看官不要叫我改用记录集的方式了。除非你指出我的 bug,而不是设计理念方面的。 各位看官不要叫我改动存储过程了。除非……
正解
赵4老师 2013-04-10
  • 打赏
  • 举报
回复
对学习编程者的忠告: 眼过千遍不如手过一遍! 书看千行不如手敲一行! 手敲千行不如单步一行! 单步源代码千行不如单步对应汇编一行! 单步类的实例“构造”或“复制”或“作为函数参数”或“作为函数返回值返回”或“参加各种运算”或“退出作用域”的语句对应的汇编代码几步后,就会来到该类的“构造函数”或“复制构造函数”或“运算符重载”或“析构函数”对应的C/C++源代码处。 VC调试时按Alt+8、Alt+7、Alt+6和Alt+5,打开汇编窗口、堆栈窗口、内存窗口和寄存器窗口看每句C对应的汇编、单步执行并观察相应堆栈、内存和寄存器变化,这样过一遍不就啥都明白了吗。 对VC来说,所谓‘调试时’就是编译连接通过以后,按F10或F11键单步执行一步以后的时候,或者在某行按F9设了断点后按F5执行停在该断点处的时候。
zjs100901 2013-04-09
  • 打赏
  • 举报
回复
命令对象、存储过程、输出参数、二进制,四个条件缺一不可。 用户觉得他没有责任为该二进制提供一个 ExpectMaxLen,用户觉得他的理想就是只给出一个空的 string,要我往里填。毕竟用户是 C++ 程序员,一个 string 引用作输出参数,怎么会要先说出长度来呢? 各位看官不要叫我改用记录集的方式了。除非你指出我的 bug,而不是设计理念方面的。 各位看官不要叫我改动存储过程了。除非你指出我的 bug,而不是设计理念方面的。 各位看官不要跟我说没必要用二进制,用字符串就行了。我是为了叙述方便才说字段内容是 abcdefg,实际上是啥都有。 m_CommandPtr 这个变量为什么用 m_ 开头,是因为我本来是有封装的,现在是为了叙述方便,整成一个文件了。 为了叙述方便,省掉了好多 try catch 语句。也没有调 Close 和 Release。
zjs100901 2013-04-09
  • 打赏
  • 举报
回复
建立表的 MySql 语句:
CREATE TABLE `bintable22` (
  `id` int(11) DEFAULT NULL,
  `bin` varbinary(16) DEFAULT NULL
)
表内容我不知道怎么用 MySql 语句来写,是在 IDE 上用键盘鼠标输入的,两条记录,第一条的 id 是 1,bin 是 abcdefg。第二条的 id 是 2,bin 是 hijklmn。 存储过程:
DELIMITER $$

USE `gametest`$$

DROP PROCEDURE IF EXISTS `GetBinInfo22`$$

CREATE DEFINER=`test`@`%` PROCEDURE `GetBinInfo22`( IN v_id INT, OUT v_bin VARBINARY(16) )
BEGIN
	SELECT `bin` INTO v_bin FROM `gametest`.`bintable22` WHERE `id` = v_id;
    END$$

DELIMITER ;
zjs100901 2013-04-09
  • 打赏
  • 举报
回复
[code=C/C++]#include <string> #include <iostream> #include <exception> using namespace std; #import "c:\Program Files\Common Files\System\ADO\msado15.dll" no_namespace rename("EOF", "EndOfFile"),rename("BOF", "BeforeOfFile") #define COM_START if(!SUCCEEDED(CoInitializeEx(NULL,COINIT_MULTITHREADED))) throw exception("初始化COM错误!"); #define COM_END CoUninitialize(); //////////////////// // 记录集 void testRecordSet_Binary(); void GetRecordSet_BinaryValue( _RecordsetPtr & m_AdoRecordSet, const char * name, std::string & retValue ); //////////////////// // 存储过程 void testProcedure_Binary(); void AddInParameter( _CommandPtr & m_CommandPtr, const char * name, long value ); void AddOutBinaryParameter( _CommandPtr & m_CommandPtr, const char * name, const char * buffer, long length ); void ExecProc( _CommandPtr & m_CommandPtr, const char * ProcName ); void GetProcedure_BinaryValue( _CommandPtr & m_CommandPtr, const char * name, std::string & retValue ); int _tmain( int argc, _TCHAR * argv[] ) { COM_START testRecordSet_Binary(); testProcedure_Binary(); COM_END return 0; } //////////////////// // 记录集 void testRecordSet_Binary() { _ConnectionPtr m_ConnectionPtr; m_ConnectionPtr.CreateInstance( __uuidof( Connection ) ); m_ConnectionPtr->Open( _bstr_t ( "Driver=MySql ODBC 5.2a Driver;Server=127.0.0.1;Database=test;UID=test;PWD=123456" ), "", "", adConnectUnspecified ); _RecordsetPtr m_AdoRecordSet; m_AdoRecordSet.CreateInstance( __uuidof( Recordset ) ); m_AdoRecordSet->PutRefActiveConnection( m_ConnectionPtr ); m_AdoRecordSet->Open( "select * from bintable22 where id=1", m_AdoRecordSet->GetActiveConnection(), adOpenDynamic, adLockOptimistic, adCmdText ); string value; GetRecordSet_BinaryValue( m_AdoRecordSet, "bin", value ); cout << value << "结束" << endl; } void GetRecordSet_BinaryValue( _RecordsetPtr & m_AdoRecordSet, const char * name, std::string & retValue ) { FieldPtr fpr = m_AdoRecordSet->GetFields()->GetItem( name ); long length = fpr->ActualSize; if( length > 0 ) { _variant_t varBLOB; _StreamPtr stream; stream.CreateInstance( "ADODB.Stream" ); _variant_t varOptional( DISP_E_PARAMNOTFOUND, VT_ERROR ); stream->raw_Open( varOptional, adModeUnknown, adOpenStreamUnspecified, NULL, NULL ); stream->put_Type( adTypeBinary ); stream->Write( fpr->GetValue() ); stream->put_Position( 0 ); varBLOB = stream->Read( adReadAll ); int len = stream->GetSize(); if( varBLOB.vt == ( VT_ARRAY | VT_UI1 ) ) ///判断数据类型是否正确 { char * pBuf = NULL; SafeArrayAccessData( varBLOB.parray, ( void ** )&pBuf ); retValue = std::string( pBuf, pBuf + length ); SafeArrayUnaccessData( varBLOB.parray ); } stream->Close(); } } //////////////////// // 存储过程 void testProcedure_Binary() { _ConnectionPtr m_ConnectionPtr; m_ConnectionPtr.CreateInstance( __uuidof( Connection ) ); m_ConnectionPtr->Open( _bstr_t ( "Driver=MySql ODBC 5.2a Driver;Server=127.0.0.1;Database=test;UID=test;PWD=123456" ), "", "", adConnectUnspecified ); _CommandPtr m_CommandPtr; m_CommandPtr.CreateInstance( __uuidof( Command ) ); m_CommandPtr->PutRefActiveConnection( m_ConnectionPtr ); long id = 1; //string bin( 16, 'x' ); // 测试该行,没问题。 string bin( 4, 'y' ); // 测试该行,有问题,引发了我写的异常。如果将我的异常那行代码删去,则最终得到的结果只有前 4 个字节。 //string bin; // 测试该行,在 AddOutBinaryParameter 的时候就挂了。如果对于空 string ,改为 AddOutBinaryParameter( ..., "\0", 1 ); 那就像上一行一样,结果只有一个字节。 AddInParameter( m_CommandPtr, "id", id ); AddOutBinaryParameter( m_CommandPtr, "bin", bin.c_str(), bin.size() ); ExecProc( m_CommandPtr, "GetBinInfo22" ); string value; GetProcedure_BinaryValue( m_CommandPtr, "bin", value ); cout << value << "结束" << endl; } void AddInParameter( _CommandPtr & m_CommandPtr, const char * name, long value ) { m_CommandPtr->Parameters->Append( m_CommandPtr->CreateParameter( _bstr_t( name ), adInteger, adParamInput, 4, value ) ); } void AddOutBinaryParameter( _CommandPtr & m_CommandPtr, const char * name, const char * buffer, long length ) { char * buf = const_cast< char * >( buffer ); VARIANT varBLOB; SAFEARRAY * psa; SAFEARRAYBOUND rgsabound[1]; rgsabound[0].lLbound = 0; rgsabound[0].cElements = length; psa = SafeArrayCreate( VT_UI1, 1, rgsabound ); ///创建SAFEARRAY对象 for( long i = 0; i < length; i++ ) SafeArrayPutElement( psa, &i, buf++ ); ///将pBuf指向的二进制数据保存到SAFEARRAY对象psa中 varBLOB.vt = VT_ARRAY | VT_UI1;///将varBLOB的类型设置为BYTE类型的数组 varBLOB.parray = psa; ///为varBLOB变量赋值 m_CommandPtr->Parameters->Append( m_CommandPtr->CreateParameter( _bstr_t( name ), adVarBinary, adParamOutput, length, varBLOB ) ); SafeArrayDestroy( psa ); } void ExecProc( _CommandPtr & m_CommandPtr, const char * ProcName ) { m_CommandPtr->CommandText = _bstr_t( ProcName ); m_CommandPtr->CommandType = adCmdStoredProc; m_CommandPtr->Execute( NULL, NULL, adCmdStoredProc ); } void GetProcedure_BinaryValue( _CommandPtr & m_CommandPtr, const char * name, string & retValue ) { _ParameterPtr ppr = m_CommandPtr->Parameters->GetItem( _bstr_t( name ) ); long ExpectMaxLen = ppr->GetSize(); if( ExpectMaxLen > 0 ) { _variant_t varBLOB; _StreamPtr stream; stream.CreateInstance( "ADODB.Stream" ); _variant_t varOptional( DISP_E_PARAMNOTFOUND, VT_ERROR ); stream->raw_Open( varOptional, adModeUnknown, adOpenStreamUnspecified, NULL, NULL ); stream->put_Type( adTypeBinary ); stream->Write( ppr->GetValue() ); stream->put_Position( 0 ); varBLOB = stream->Read( adReadAll ); int ActualLen = stream->GetSize(); if( ActualLen > ExpectMaxLen ) { // AddOutBinaryParameter 时提供的长度就是 ExpectMaxLen 的值 // ActualLen 是字段实际的有效内容的长度(无论 ExpectMaxLen 比 ActualLen 大还是小),也是 varBLOB.parray 的长度 // 当 ExpectMaxLen 设置得不够大时,varBLOB.parray 就只有前 ExpectMaxLen 个字节有正确的内容 // 例如字段实际的内容为 7 字节长 // AddOutBinaryParameter( ..., 16 ),则 ExpectMaxLen 的值是 16,ActualLen 的值是 7,varBLOB.parray 的 7 个字节均为有效数据 // AddOutBinaryParameter( ..., 4 ),则 ExpectMaxLen 的值是 4,ActualLen 的值是 7,varBLOB.parray 的 7 个字节中,前 4 个字节为有效数据,后 3 个字节的值为 \0 或乱码 throw exception( "期望获得较小的长度,而实际上字段的有效内容的长度较大,没有取得全部数据!" ); } if( varBLOB.vt == ( VT_ARRAY | VT_UI1 ) ) ///判断数据类型是否正确 { char * pBuf = NULL; SafeArrayAccessData( varBLOB.parray, ( void ** )&pBuf ); retValue = std::string( pBuf, pBuf + ActualLen ); SafeArrayUnaccessData( varBLOB.parray ); } stream->Close(); } }[/Code]

64,646

社区成员

发帖
与我相关
我的任务
社区描述
C++ 语言相关问题讨论,技术干货分享,前沿动态等
c++ 技术论坛(原bbs)
社区管理员
  • C++ 语言社区
  • encoderlee
  • paschen
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
  1. 请不要发布与C++技术无关的贴子
  2. 请不要发布与技术无关的招聘、广告的帖子
  3. 请尽可能的描述清楚你的问题,如果涉及到代码请尽可能的格式化一下

试试用AI创作助手写篇文章吧