为什么我不能在为 Windows CE 创建的 MFC DLL 中使用 CString 作为返回类型或参数?

Why cannot I use CString as return types or parameters in a MFC DLL made for windows CE?

提问人:PRinCEKtd 提问时间:5/31/2016 最后编辑:PRinCEKtd 更新时间:6/1/2016 访问量:898

问:

我有一个为桌面 Windows 项目制作的库。它是由其他人在 MFC VC++ 中完成的,并且工作正常。我将使用库中的一个特定函数作为示例来解释这种情况。

示例函数如下所示:

CString GetFulPath(); // .h file  

在 cpp 文件中,

CString CwFolderBrowser::GetFullPath()
{
    CString path;
    if(this->M_pIDLIST!=NULL)
    {
        LPTSTR fullPath=path.GetBuffer(MAX_PATH);
        ::SHGetPathFromIDList(this->M_pIDLIST, fullPath);   //ITEMIDLISTからパスを得る
        path.ReleaseBuffer();
    }
    return path;
}

现在,我可以将这个库包含在我的项目中,并执行如下操作:

CwFolderBrowser cFolderBrowser;
if(cFolderBrowser.ShowDialog() == TRUE)
     cPath = cFolderBrowser.GetFullPath();

这将显示一个文件夹浏览器对话框,让我选择一个文件夹。它在桌面窗口上运行良好。

目前,我正在使用 Windows CE 设备。我们通过删除不支持的函数和内容来转换该库以用于 Windows CE。该库可以正确编译和构建,而不会出现错误。

接下来,我创建一个 MFC 智能设备项目,包括转换后的库、其 h 文件和 lib 文件,并为 dll 设置适当的目录。项目构建良好。我也可以正确 #include 库的 h 文件。

一旦我调用 GetFullPath 函数,问题就会出现:

cPath = cFolderBrowser.GetFullPath();  

它给了我一个未解决的外部链接错误!Intellisense 确实在其列表中显示了此函数,我可以选择它和所有内容。但徒劳无功。

奇怪的是,如果我修改库并更改 GetFullPath() 的签名,如下所示,

LPCTSTR GetFulPath(); // .h file  LPCTSTR instead of CString

在 cpp 文件中,

LPCTSTR CwFolderBrowser::GetFullPath()  // Return type changed to LPCTSTR  
{                                       // instead of CString 
     ...                                // Body modified accordingly
}

然后,未解决的外部链接错误消失,它起作用了!

我对这种奇怪的行为感到困惑,因为,我可以在 MFC 智能设备项目中正常使用 CString,并且没有错误。仅当我尝试从库(和其他此类库)dll 调用函数时,才会显示链接错误。同时,BOOL、int 等作为函数返回类型似乎没有问题。

当然,我可以遍历每个库并将 CString 返回的每个实例更改为 LPCTSTR,但这将是一个非常大的更改。我想知道为什么 CString 在桌面上时在项目和 dll 中都能正常工作,而在 Win CE 上,它在项目中工作,但在 DLL 中不起作用(同时,DLL 本身编译良好,无论它使用 CString 还是 LPCTSTR!

所以,基本上,如果可能的话,我想保留函数 CString,并想知道发生这种情况的原因。完全相同的错误也发生在其他库中。

任何帮助都是值得赞赏的。谢谢。

更新:我在 ATL 和 MFC 7.0 上看到一个页面,其中谈到了使用 /Zc:wchat_t 选项。我已经检查了Dll项目以及我的应用程序。两者都使用相同的选项“将wchar_t视为内置类型”作为“是”。因此,该选项匹配。 此外,正如我上面提到的,将函数返回更改为 LPCTSTR 是有效的。错误消失。一切顺利,直到我将返回的 LPCTSTR 转换回 CString。CString 结果为空/Null。这既发生在 dll 代码本身内部,也发生在我的应用程序代码中。

UPDATE2:感谢 Michael 和 Cody,我将函数更改为 LPCTSTR,并确保这些值不会超出范围,然后才能像他们建议的那样使用它们。现在空/Null 问题解决了,我可以正确获取路径值。

剩下的问题是我必须将所有CString函数转换为LPCTSTR,这并不完全可行。我想将函数保留为 CString

visual-c++ dll MFC 链接器错误 未解决-外部

评论


答:

3赞 Jabberwocky 5/31/2016 #1

这是一个经典的问题,在 SO 上经常被问到。

这不起作用:

LPCTSTR CwFolderBrowser::GetFullPath()
{
    CString path;
    if(this->M_pIDLIST!=NULL)
    {
        LPTSTR fullPath=path.GetBuffer(MAX_PATH);
        ::SHGetPathFromIDList(this->M_pIDLIST, fullPath);  
        path.ReleaseBuffer();
    }
    return (LPCTSTR)path;  // << here you return a pointer to the zero 
                           //    terminated string in the path object,  
}                          //    but path will be deleted as soon as it goes
                           //    out of scope

也许在某些情况下,该函数似乎可以工作,因为已删除的 CString 对象的内存尚未被覆盖。

您应该这样做(为简单起见,此处没有错误处理):

LPCTSTR CwFolderBrowser::GetFullPath(TCHAR *pathbuffer)
{
    if(this->M_pIDLIST!=NULL)
    {
        ::SHGetPathFromIDList(this->M_pIDLIST, pathbuffer);  
    }

    return (LPCTSTR)pathbuffer;
}

...
// call like this
TCHAR pathbuffer[MAX_PATH];
GetFullPath(pathbuffer);

评论

0赞 PRinCEKtd 5/31/2016
谢谢迈克尔。这个奏效了。在桌面情况下,返回类型是 CString,所以,我认为返回“路径”变量很好。但是在 CE 的情况下,当它返回一个指针时,我们必须改变一些东西......无论如何,我尝试了您的建议,这部分工作正常。现在问题来了,为什么在 Windows CE 的情况下不能使用 CString 作为此函数的返回类型?
0赞 PRinCEKtd 5/31/2016
如问题中的第一个示例所示,这是 Windows 桌面版本的 Dll 中的原始函数。它编译良好且工作正常。但是当我尝试为 Windows CE 编译它时,CString 返回类型会导致未解析的链接器错误,这就是我尝试将其从 CString 更改为 LPCTSTR 的原因。如果 CString 返回类型在 Windows CE 中也有效,那么我可以直接按原样使用原始函数本身,而不必将其更改为 LPCTSTR。
0赞 Jabberwocky 5/31/2016
@PRinCEKtd这个问题我没有答案,你实际上有两个不同的问题。
0赞 PRinCEKtd 5/31/2016
嗯......似乎是这样,不是吗?我应该将问题分解为 2 个不同的问题吗?
3赞 Cody Gray - on strike 5/31/2016
哦,我不会这样做。这创建了一个非常令人困惑的 API。如果我看到这个,我就无法知道你正在向我返回我传入的同一缓冲区的句柄,或者另一个缓冲区的句柄。通常,当您从函数返回指针时,这意味着调用方拥有该指针的所有权并负责销毁它。但是由于我已经拥有了,我最终可能会获得双重释放。我必须阅读文档才能确定(如果存在)。只需使用单个输入/输出参数即可。这一点要清楚得多。以 BOOL 或 HRESULT 的形式返回成功。pathbuffer