0

我正在制作一个使用 C# 托管 DLL 的非托管 C++ DLL。我正在编写 C++ 库,因为我需要使用在软件中定义的函数和头文件,C++ 库可以作为插件添加。但是我想做的事情太复杂了,以至于我对 C++ 的了解会减慢我的速度,所以我决定用我最喜欢的 C# 做事情,并通过 COM 连接 DLL,我成功了。

我在使代码工作方面取得了某种成功,但在保持代码简洁方面不太成功,因为我显然不是专业的 C++ 程序员。

问题在于转换各种字符串类型。特别是 BSTR 和 const char *。

以下代码将 const char * 转换为 BSTR:

BSTR bstrt;
const char * someChar;

csharpInterfacedClassPointer->get_PropertyForSomeChars(&bstrt);
strcpy_s(nstring, (char *)bstrt);
someChar = nstring;

问题是,我有很多离散的 someChars 和相应的离散接口方法......属性方法是从 C# 接口生成的,所以我无法更改它。每个“someChar”都需要以下三行代码,因此对于 30 个离散变量,我需要编写 90 行代码。

csharpInterfacedClassPointer->get_PropertyForSomeCharX(&bstrt);
strcpy_s(nstring, (char *)bstrt);
someCharX = nstring;

问题是:如何为此编写一些快捷方式以便它只适合一行?

我用“getter”函数指针和 someChar 指针尝试了某种函数。

typedef HRESULT (__stdcall *get_string_func)(BSTR * str); //getter function pointer

//the converting function
void ConvertAndAssign(get_string_func bstr_get_fx, const char * constCharString) 
{
const size_t nsize = 1000;
char nstring[nsize];

BSTR bstrt;
bstrt = bstr_t(constCharString);
bstr_get_fx(&bstrt);
strcpy_s(nstring, (char *)bstrt);
constCharString = nstring;
}

//calling the function...as I thought that would work
ConvertAndAssign(sPtr->get_DataFile, someChar);

但是随后编译器说了一些关于绑定函数的奇怪事情,以及它们如何不允许作为指针......我用谷歌搜索了它的含义以及更改函数定义所需的解决方案,但我不能这样做,因为定义是生成的来自 C# 代码(通过 regasm.exe)。

重要说明:最后我需要获取 const char * 类型,因为它是我正在为其制作 C++ DLL 的程序的函数所需的输入类型。

4

2 回答 2

1
Disclaimer: it was a long time (7 years to be more precise) since I have touched C++/COM code for the last time.

关于将实例方法绑定到函数指针检查这个SO question。

另一种选择是使用IDispatch接口(如果您的 COM 组件实现它)

关于 ConvertAndAssign() 实现,IMO 它有一些问题。为了便于解释,我将其复制如下:

void ConvertAndAssign(get_string_func bstr_get_fx, const char * constCharString) 
{
    const size_t nsize = 1000;
    char nstring[nsize];

    BSTR bstrt;
    bstrt = bstr_t(constCharString); // issue 1
    bstr_get_fx(&bstrt);
    strcpy_s(nstring, (char *)bstrt); // issue 2
    constCharString = nstring; // issues  3 & 4
} 
  1. 如果您的 COM 方法返回 BSTR(即,它具有 BSTR 类型的 out 参数),则不应传递预分配的字符串,否则最终会导致内存泄漏。

  2. 将 bstr转换为 char * 将不起作用。BSTR 是 Unicode 字符串。它们也被编码,使其长度在实际字符之前。如果您使用 ATL/MFC,您可以使用字符串转换宏之一。如果您不使用 ATL/MFC,您可以使用WideCharToMultiByte()函数或“智能”BSTR 类之一(ATL 中的 CComBSTR、bstr_t 等)

  3. 将nstring分配给constCharString对外部字符串没有影响。如果您正在调用 ConvertAndAssign 如下

    char *outsideStr = NULL;              
    ConvertAndAssign(whatever, outsideStr);
    

    ConvertAndAssign() 函数内部consCharString将在开始时指向 NULL。在赋值之后constCharString确实指向nstringoutsideStr仍然指向 NULL(请记住,当您调用 ConvertAndAssign() 函数时,指针值的副本被传递给它)。

    为了得到你想要的,你可以传递一个引用或一个指向指针的指针:

     void ConvertAndAssign(get_string_func bstr_get_fx, const char * &constCharString) 
     {
          constCharString = nstring; // Assignment
     }   
    

    或指向指针的指针:

     char *outsideStr = NULL;
     ConvertAndAssign(whatever, &outsideStr);
    
     void ConvertAndAssign(get_string_func bstr_get_fx, const char **constCharString) 
     {
          .
          .
          .
          *constCharString = nstring; // Assignment
     }
    
  4. 解决上一个问题后,您将遇到另一个问题:您不能返回局部变量的地址!当您的代码在 ConvertAndAssign() 返回后恢复时,不再为您分配此地址(它是堆栈的一部分,因此它甚至可能看起来正在工作,但我向您保证,它不是;您的代码中最轻微的变化可能打破它)

    要解决此问题,您需要传递一个预先分配的字符串:

    char outsideStr[1000];
    ConvertAndAssign(whatever, outsideStr);
    
    void ConvertAndAssign(get_string_func bstr_get_fx, const char * constCharString) 
    {
        strcpy_s(constCharString, /* result str here */ );
    }
    

    或在堆中分配一个字符串。

鉴于以上所有情况,ConvertAndAssign() 的一种可能实现及其用法如下:

char outsideStr[1000];
ConvertAndAssign(whatever, outsideStr);

void ConvertAndAssign(get_string_func bstr_get_fx, const char * constCharString) 
{
    BSTR bstrt;
    if(SUCCEEDED(bstr_get_fx(&bstrt)))
    {
         // Assumes constCharString points to a buffer large enough to hold the converted string.
        strcpy_s(constCharString, CW2A(bstr)); // not completely correct since bstr may contain the byte 0 but I guess thats not your scenario.
        SysFreeString(bstr);
    }
    else
    {
         // error handling.
         constCharString[0] = 0;
    }
}
于 2012-09-10T20:36:03.220 回答
0

如果有任何模组要阅读此问题,则可以删除此问题。我意识到我有一个完全不同的问题。感谢 Vagaus 的努力。

于 2012-09-11T15:25:01.480 回答