【发布时间】:2017-04-03 02:53:13
【问题描述】:
目前,我正在开发一个中型内部应用程序,其中应该包含一些已包含在以前的应用程序中的功能。这是多年前由外部供应商在 Visual Basic 6 中开发的。不幸的是,我无法访问源代码,但幸运的是,必要的类包含在 COM DLL 中,该 DLL 也是在 VB6 中创建的。目前使用的开发环境是C++ Builder 10.1。我可以通过 regsvr32.exe 轻松安装 DLL。 CBuilder 中的类型库导入功能基于TOleServer 生成了一个很好的VCL 包装器。 DLL 非常庞大,导入后已经存在丢失函数重载的问题,我不得不手动添加。否则,包装器似乎可以工作。
现在是真正的问题:
DLL 包含一个类,其字段可以通过称为 Load 的方法从 XML 文件中加载。此函数有 2 个参数:VB6 和 VBA 中的 File As String 和 Schema As String(使用 Excel 2010 检查)。参数 Schema 是可选的。如果我现在在 VBA 中创建一个类对象,我可以将文件名字符串作为 File 参数传递,并且类字段的加载没有问题。 如果我查看 CBuilder 中生成的包装类,Load 函数有两个“BSTR *”类型的参数。 Schema 参数在自动生成的注释中被标记为 optional,但没有分配默认值,因此不是可选的 (?)。所以我必须使用参数Schema。奇怪的是,需要一个 指向 BSTR 的指针,而不是 BSTR。所以我尝试了以下方法:
BSTR File = SysAllocString(L"C:\\temp\\file.xml");
BSTR Schema = SysAllocString(L"");
TMyOleClass *MyClass = new TMyOleClass(this);
MyClass->Load(&File, &Schema);
由于架构文件通常不被使用,所以我没有。因此它是一个 zero 字符串。
类的创建没有错误,但 Load 函数在 MSVBVM60.dll 中引发 Float Divide By Zero 错误。其他需要普通 BSTR(不是指向它的指针)的类函数可以正常工作。
所以...
- 为什么第二个参数在生成的 VCL 包装器中不是可选的?
- 为什么参数是 BSTR* 类型而不是 BSTR 类型,和其他参数一样?
- 为什么会出现上述错误?
感谢所有回答。
编辑:
我已经用 oleview.exe 打开了课程。 Load 函数的定义如下:
HRESULT Load(
[in, out] BSTR* Filepath,
[in, out, optional] BSTR* Schema,
[out, retval] VARIANT* );
尽管仅在 Excel VBA 对象目录中
Function Load(Filepath As String, [Schema As String])
显示(无参考)。
将架构参数设置为 NULL 的想法不起作用。 出现了同样的 div 错误。
感谢@Remy Lebeau,我创建了一个变体并再次尝试:
BSTR Path = SysAllocString(L"C:\\temp\\file.xml");
VARIANT varOpt;
varOpt.vt = VT_ERROR;
varOpt.scode = DISP_E_PARAMNOTFOUND;
TC_MyClass *MyClass = new TC_MyClass(this);
MyClass->Load(&Path, (BSTR*)&varOpt);
-->同样的错误。
我发现,如果使用 Continue 跳过 Exceptions 3 次,则会出现错误提示:此数组已修复或暂时锁定。
以下是一些图片;
The code line in utilcls.h after that the error is being raised
以下是 CBuilder 生成的 VCL-Wrapper 的一些部分:
class PACKAGE TC_MyClass : public Vcl::Oleserver::TOleServer
{
_C_MyClassPtr m_DefaultIntf;
_di_IUnknown __fastcall GetDunk();
public:
__fastcall TC_MyClass(System::Classes::TComponent* owner) : Vcl::Oleserver::TOleServer(owner)
{}
...
VARIANT __fastcall Load(BSTR* Filepath/*[in,out]*/, BSTR* Schema/*[in,out,opt]*/)
{
VARIANT Param3;
OLECHECK(GetDefaultInterface()->Load(Filepath, Schema, (VARIANT*)&Param3));
return Param3;
}
...
@Remy Lebeau:我必须手动更改的功能是
HRESULT __fastcall set_Sections(MyHugeAndComplex_dll_tlb::_E_SectionsPtr* Param1/*[in,out]*/)
{
return set_Sections((MyHugeAndComplex_dll_tlb::_E_Sections*)Param1/*[in,out]*/);
}
我必须添加第二个实现
HRESULT __fastcall set_Sections(MyHugeAndComplex_dll_tlb::_E_SectionsPtr** Param1/*[in,out]*/)
{
return set_Sections(Param1/*[in,out]*/);
}
我还必须为第二个功能添加它:
HRESULT __fastcall set_Document(Msxml2_tlb::IXMLDOMDocument2Ptr* Param1/*[in,out]*/)
{
return set_Document((Msxml2_tlb::IXMLDOMDocument2*)Param1/*[in,out]*/);
}
编辑 2:
我在 LabVIEW 中创建了一个 VI,它创建了一个类的实例,并且可以毫无问题地调用“加载”函数。
现在,当使用导出的包装函数调用“Load”将创建的 VI 本身包装在新的 Win32 DLL 中并通过“SafeLoadLibrary”调用在 CBuilder 中通过代码打开此 DLL 时,会发生相同的浮点错误,即使我在 LabVIEW-VI 中硬编码 XML 的路径。我几乎可以从任何地方调用“加载”函数,C++ Builder 除外。 想一想,如果这可能是 CBuilder 中的错误而不是我的错...
String DLL_FileName = ExtractFilePath(Application->ExeName) + "MyNewCreatedDLLWrapper.dll";
HINSTANCE hInstance = (HINSTANCE)SafeLoadLibrary(DLL_FileName.w_str());
if(!hInstance)
throw(Exception("Error loading DLL"));
Load = (Load_Ptr)GetProcAddress(hInstance,"Load");
int32_t Length = 1000;
uint8_t Array[1000];
int32_t RetVal = Load("", Array, &Length); // <-- path can be omitted as it is hard coded in DLL
FreeLibrary(hInstance);
【问题讨论】:
-
可能可选参数必须为 NULL,在这种情况下,与 Schema 相关的变量将被初始化为一些默认值。但是,因为您提供了一个空字符串,它会将变量初始化为零,而不是尝试解释 Schema;它失败了,因为它是一个空字符串,但是应用程序没有检测到这个错误,因为他们没有预料到它。然后稍后它会尝试在除法中使用浮点变量,例如纵横比 = 宽度/高度,但宽度和高度均为 0.0。
-
相关参数的“入、出”属性是什么
-
如果您发布 Load 函数的 IDL 定义以及自动生成的函数标头会有所帮助
-
尝试将第二个参数设为NULL
-
我现在在周末。我将在星期一回到我的工作机器时添加所需的信息。
标签: c++ com c++builder vcl bstr