混淆在使用BSTR在com dll在c++构建器

Confused in used BSTR in com dll in c++ builder

本文关键字:dll c++ 构建 com BSTR      更新时间:2023-10-16

我在c++ Builder Borland中制作了一个OCX。我的OCX有如下功能:

  • 登录
  • DebugOutput
  • GetVal

我在我的c#应用程序中使用这个OCX。在c#方面,我有一个按钮和列表框。按钮从OCX调用login方法,列表框显示该方法的输出。

在OCX端,login方法为服务器(使用套接字编程)创建一个命令来获得身份验证。然后调用Write函数写入套接字。然后从套接字获取响应,调用Read函数读取套接字响应。

Read方法读取结果并将其发送给DebugOutput以调试输出流并调用GetVal以查找最后的主服务器响应。然后它们相互传递参数。最后c#端显示登录方法的结果(SUCCESS | FAIL)。

我使用BSTR。(我在stackoverflow和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"));
   }

您不应该返回BSTR(或任何各种编译器以各种方式编译的"复杂"类型)。COM方法的推荐返回类型为HRESULT(32位整数)。此外,您不能释放刚刚分配的BSTR并将其返回给调用者。

你可以这样布局你的方法:

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")

你的代码中有很多错误:

  • if(read!=NULL) ::SysFreeString(read); return read;释放一个字符串,然后返回它
  • str= e.Message.c_str();return str;返回一个指向甚至不是BSTR
  • 的东西的指针
  • BSTR str2=::SysAllocString(L"TST"); str2=Read();分配一个字符串,然后立即泄漏该字符串
  • res=(BSTR)WideString(stAnsi);创建悬浮指针

所有这些都是未定义的行为(除了泄漏),并可能导致崩溃。

另外,我不确定是否有你的函数返回BSTR是有效的;COM编程中的常规约定是函数返回HRESULT,任何"返回值"都通过[out]参数返回。这与所有具有COM绑定的语言兼容;而实际上返回BSTR限制了代码兼容性。(我可能是错的——欢迎指正)。你可以在你链接的另一个问题中看到这种技巧。


要获得关于"为什么我的代码崩溃了?"这样的问题的帮助,请参见如何询问和如何创建一个最小的,完整的和可验证的示例。