在 c++ builder 的 com dll 中使用 BSTR 时感到困惑
Confused in used BSTR in com dll in c++ builder
我在 C++ Builder Borland 中制作了一个 OCX。我的 OCX 具有我描述的这些功能:
- 登录
- 写
- 阅读
- 调试输出
- 获取值
我在我的 C# 应用程序中使用这个 OCX。在 C# 端我有一个按钮和列表框。该按钮从 OCX 调用 login
方法,列表框显示此方法的输出。
在 OCX 端,login
方法为服务器(使用套接字编程)创建一个命令以获取身份验证。然后调用Write
函数写入socket。然后从套接字获取响应并调用 Read
函数读取套接字响应。
Read
方法读取结果并将其发送到DebugOutput
以调试输出流并调用GetVal
以查找最后的主服务器响应。然后他们将参数传递给对方。毕竟 C# 端显示登录方法的结果 (SUCCESS | FAIL)。
我用的是BSTR。 (我在 Whosebug and on MSDN 中阅读了有关 BSTR 的主题,但我认为我在我的解决方案中没有很好地理解它)。这些是我在 OCX 端的代码:
BSTR STDMETHODCALLTYPE TVImpl::Login(BSTR PassWord)
{
wchar_t wcs1[500];
Var *var=new Var();
//here make the command
...................
//get the server response to show to user
BSTR read=::SysAllocString(Write(wcs1));
if(read!=NULL) ::SysFreeString(read);
return read;
}
BSTR STDMETHODCALLTYPE TVImpl::Read()
{
BSTR str ;
try
{
IdTCPClient1->ReadTimeout=100;
str =::SysAllocString( IdTCPClient1->IOHandler->ReadLn().c_str());
}
catch(Exception &e)
{
str= e.Message.c_str();
}
str=(DebugOutput(str));
return str;
}
BSTR STDMETHODCALLTYPE TVImpl::Write(BSTR str)
{
IdTCPClient1->IOHandler->WriteLn(str) ;
BSTR str2=::SysAllocString(L"TST");
str2=Read();
return str2;
}
BSTR STDMETHODCALLTYPE TVImpl::GetVal(BSTR st,BSTR ValTyp)
{
BSTR res;
AnsiString stAnsi;
//Do some thing with st and save it to stAnsi
.................
res=(BSTR)WideString(stAnsi);
return ::SysAllocString(res);
}
BSTR STDMETHODCALLTYPE TVImpl::DebugOutput(BSTR st)
{
Var *val=new Var();
BSTR res;
res=GetVal(st,val->CMD_CMD);
if(res==val->CMD_AUTHENTICATE)
res=GetVal(st,val->XPassword);
return res;
}
在 C3 代码中它被挂起。我知道我的问题是使用 sysAllocString。但是当我再次在每个方法中对每个方法使用 ::sysFreestring 时,我的 C# 代码挂起。
这是我的 C# 代码:
private void button4_Click(object sender, EventArgs e)
{
VinSockCmplt.Vin vin = new Vin();
listBox1.Items.Add(vin.Login("1234"));
}
你的代码有很多错误:
if(read!=NULL) ::SysFreeString(read); return read;
释放一个字符串,然后 return 释放它
str= e.Message.c_str();
... return str;
return 指向甚至不是 BSTR 的东西的指针
BSTR str2=::SysAllocString(L"TST"); str2=Read();
分配一个字符串,然后立即泄漏该字符串
res=(BSTR)WideString(stAnsi);
创建一个悬挂指针
所有这些都是未定义的行为(泄漏除外),可能会导致崩溃。
此外,我不确定您的函数是否有效 return BSTR
; COM 编程中的常规约定是函数 return HRESULT
和任何 "return values" 通过 [out]
参数返回。这与所有具有 COM 绑定的语言兼容;而实际上 returning BSTR
限制了您的代码兼容性。 (我可能是错的 - 欢迎在这里更正)。您可以在 the other question you linked.
中看到此技术的使用
要获得有关 "Why is my code crashing?" 等问题的帮助,请参阅 How to Ask and How to create a Minimal, Complete, and Verifiable example。
您不应该 return BSTR(或各种编译器以各种方式编译的任何 "complex" 类型)。 COM 方法的推荐 return 类型是 HRESULT(32 位整数)。此外,您不得释放刚刚分配的 BSTR 并将其return 释放给调用者。
以下是您如何布局您的方法:
HRESULT STDMETHODCALLTYPE TVImpl::Login(BSTR PassWord, /*out*/ BSTR *pRead)
{
...
*pRead = ::SysAllocString(L"blabla"); // allocate a BSTR
...
// don't SysFreeString here, the caller should do it
...
return S_OK;
}
如果您在 .idl 文件中定义您的界面,它将类似于:
interface IMyInterface : IUnknown
{
...
HRESULT Login([in] BSTR PassWord, [out, retval] BSTR *pRead);
...
}
retval
用于指示赋值语义对于支持它的语言是可能的,就像你最初试图用这样的代码实现的 var read = obj.Login("mypass")
我在 C++ Builder Borland 中制作了一个 OCX。我的 OCX 具有我描述的这些功能:
- 登录
- 写
- 阅读
- 调试输出
- 获取值
我在我的 C# 应用程序中使用这个 OCX。在 C# 端我有一个按钮和列表框。该按钮从 OCX 调用 login
方法,列表框显示此方法的输出。
在 OCX 端,login
方法为服务器(使用套接字编程)创建一个命令以获取身份验证。然后调用Write
函数写入socket。然后从套接字获取响应并调用 Read
函数读取套接字响应。
Read
方法读取结果并将其发送到DebugOutput
以调试输出流并调用GetVal
以查找最后的主服务器响应。然后他们将参数传递给对方。毕竟 C# 端显示登录方法的结果 (SUCCESS | FAIL)。
我用的是BSTR。 (我在 Whosebug and on MSDN 中阅读了有关 BSTR 的主题,但我认为我在我的解决方案中没有很好地理解它)。这些是我在 OCX 端的代码:
BSTR STDMETHODCALLTYPE TVImpl::Login(BSTR PassWord)
{
wchar_t wcs1[500];
Var *var=new Var();
//here make the command
...................
//get the server response to show to user
BSTR read=::SysAllocString(Write(wcs1));
if(read!=NULL) ::SysFreeString(read);
return read;
}
BSTR STDMETHODCALLTYPE TVImpl::Read()
{
BSTR str ;
try
{
IdTCPClient1->ReadTimeout=100;
str =::SysAllocString( IdTCPClient1->IOHandler->ReadLn().c_str());
}
catch(Exception &e)
{
str= e.Message.c_str();
}
str=(DebugOutput(str));
return str;
}
BSTR STDMETHODCALLTYPE TVImpl::Write(BSTR str)
{
IdTCPClient1->IOHandler->WriteLn(str) ;
BSTR str2=::SysAllocString(L"TST");
str2=Read();
return str2;
}
BSTR STDMETHODCALLTYPE TVImpl::GetVal(BSTR st,BSTR ValTyp)
{
BSTR res;
AnsiString stAnsi;
//Do some thing with st and save it to stAnsi
.................
res=(BSTR)WideString(stAnsi);
return ::SysAllocString(res);
}
BSTR STDMETHODCALLTYPE TVImpl::DebugOutput(BSTR st)
{
Var *val=new Var();
BSTR res;
res=GetVal(st,val->CMD_CMD);
if(res==val->CMD_AUTHENTICATE)
res=GetVal(st,val->XPassword);
return res;
}
在 C3 代码中它被挂起。我知道我的问题是使用 sysAllocString。但是当我再次在每个方法中对每个方法使用 ::sysFreestring 时,我的 C# 代码挂起。
这是我的 C# 代码:
private void button4_Click(object sender, EventArgs e)
{
VinSockCmplt.Vin vin = new Vin();
listBox1.Items.Add(vin.Login("1234"));
}
你的代码有很多错误:
if(read!=NULL) ::SysFreeString(read); return read;
释放一个字符串,然后 return 释放它str= e.Message.c_str();
...return str;
return 指向甚至不是 BSTR 的东西的指针BSTR str2=::SysAllocString(L"TST"); str2=Read();
分配一个字符串,然后立即泄漏该字符串res=(BSTR)WideString(stAnsi);
创建一个悬挂指针
所有这些都是未定义的行为(泄漏除外),可能会导致崩溃。
此外,我不确定您的函数是否有效 return BSTR
; COM 编程中的常规约定是函数 return HRESULT
和任何 "return values" 通过 [out]
参数返回。这与所有具有 COM 绑定的语言兼容;而实际上 returning BSTR
限制了您的代码兼容性。 (我可能是错的 - 欢迎在这里更正)。您可以在 the other question you linked.
要获得有关 "Why is my code crashing?" 等问题的帮助,请参阅 How to Ask and How to create a Minimal, Complete, and Verifiable example。
您不应该 return BSTR(或各种编译器以各种方式编译的任何 "complex" 类型)。 COM 方法的推荐 return 类型是 HRESULT(32 位整数)。此外,您不得释放刚刚分配的 BSTR 并将其return 释放给调用者。
以下是您如何布局您的方法:
HRESULT STDMETHODCALLTYPE TVImpl::Login(BSTR PassWord, /*out*/ BSTR *pRead)
{
...
*pRead = ::SysAllocString(L"blabla"); // allocate a BSTR
...
// don't SysFreeString here, the caller should do it
...
return S_OK;
}
如果您在 .idl 文件中定义您的界面,它将类似于:
interface IMyInterface : IUnknown
{
...
HRESULT Login([in] BSTR PassWord, [out, retval] BSTR *pRead);
...
}
retval
用于指示赋值语义对于支持它的语言是可能的,就像你最初试图用这样的代码实现的 var read = obj.Login("mypass")