【问题标题】:Confused in used BSTR in com dll in c++ builder在 c++ builder 中的 com dll 中使用 BSTR 感到困惑
【发布时间】:2017-01-20 16:23:41
【问题描述】:

我在 C++ Builder Borland 中制作了一个 OCX。正如我所描述的,我的 OCX 具有以下功能:

  • 登录
  • 阅读
  • 调试输出
  • GetVal

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

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

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

我使用了 BSTR。 (我在 stackoverflowMSDN 上阅读了有关 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"));
   }

【问题讨论】:

  • 所以你的 c# 代码挂了,但你还没有显示出来......
  • 没有必要粗鲁。
  • 对不起,我没有恶意
  • 您忘记使用 ::sysFressstring。
  • 我说。这是一个悬而未决的收获。

标签: c++ com bstr


【解决方案1】:

您不应返回 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")

【讨论】:

  • 非常感谢您的帮助。我这样做:*pRead = ::SysAllocString(L"blabla"); // allocate a BSTR 但此错误显示为 Cannot convert 'wchar_t *' to 'wchar_t * *'BSTR 需要 * 吗?
  • @MHM 该错误消息与该代码不对应。也许你不小心把BSTR **pRead 放在了参数中;或者你写了BSTR *pRead =
  • @M.M 当我在 .ridl 中创建方法并将参数放入 [out,retval] 时,它显示此错误:输出参数需要指针类型。然后我将 BSTR 更改为 BSTR* 。 Ans 这是我的方法。STDMETHODIMP TVinaImpl::tst(BSTR* p) { try { p=::SysAllocString(L"Test bstr232"); } catch(Exception &e) { return Error(e.Message.c_str(), IID_IVina); } 返回 S_OK; } 但此错误显示:无法将 'wchar_t *' 转换为 'wchar_t * *'
  • @MHM 应该是 *p = 。注重细节
【解决方案2】:

你的代码有很多错误:

  • 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 会限制您的代码兼容性。 (我可能是错的 - 欢迎在这里更正)。您可以在the other question you linked 中看到这种技术。


要就“为什么我的代码崩溃?”之类的问题获得帮助,请参阅 How to AskHow to create a Minimal, Complete, and Verifiable example

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-15
    • 2018-01-11
    • 2011-02-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多