Currently, I'm developing a mid-sized in-house application, which should include some features that are already included in a previous application. This has been developed in Visual Basic 6 many years ago by an external supplier. Unfortunately I don't have access to the source code, but fortunately, the necessary classes are contained in a COM DLL, which was also created in VB6. The currently used development environment is C++ Builder 10.1. I can easily install the DLL via regsvr32.exe.
The type library import function in CBuilder has generated a nice VCL wrapper based on TOleServer. The DLL is quite huge, and after the import there was already a problem with a missing function overload, which I had to add by hand. Otherwise, the wrapper seems to work.
Now to the real problem:
The DLL contains a class whose fields can be loaded from an XML file by a method called Load. This function has 2 parameters: File As String and Schema As String in VB6 and VBA (checked with Excel 2010). The parameter Schema is optional. If I now create a class object in VBA, I can pass a filename string as the File param and the class fields are loaded without problems.
If I look at the generated wrapper class in CBuilder, the Load function has two parameters of the "BSTR *" type. The Schema parameter is marked as optional in the automatically generated comment, but has not assigned a default value and is therefore not optional (?). So I have to use the parameter Schema. Strangely, a pointer to BSTR is expected instead of BSTR. So I tried the following:
BSTR File = SysAllocString(L"C:\\temp\\file.xml");
BSTR Schema = SysAllocString(L"");
TMyOleClass *MyClass = new TMyOleClass(this);
[in, out] BSTR* Filepath,
[in, out, optional] BSTR* Schema,
[out, retval] VARIANT* );
Function Load(Filepath As String, [Schema As String])
BSTR Path = SysAllocString(L"C:\\temp\\file.xml");
varOpt.vt = VT_ERROR;
varOpt.scode = DISP_E_PARAMNOTFOUND;
TC_MyClass *MyClass = new TC_MyClass(this);
class PACKAGE TC_MyClass : public Vcl::Oleserver::TOleServer
_di_IUnknown __fastcall GetDunk();
__fastcall TC_MyClass(System::Classes::TComponent* owner) : Vcl::Oleserver::TOleServer(owner)
VARIANT __fastcall Load(BSTR* Filepath/*[in,out]*/, BSTR* Schema/*[in,out,opt]*/)
OLECHECK(GetDefaultInterface()->Load(Filepath, Schema, (VARIANT*)&Param3));
HRESULT __fastcall set_Sections(MyHugeAndComplex_dll_tlb::_E_SectionsPtr* Param1/*[in,out]*/)
HRESULT __fastcall set_Sections(MyHugeAndComplex_dll_tlb::_E_SectionsPtr** Param1/*[in,out]*/)
HRESULT __fastcall set_Document(Msxml2_tlb::IXMLDOMDocument2Ptr* Param1/*[in,out]*/)
String DLL_FileName = ExtractFilePath(Application->ExeName) + "MyNewCreatedDLLWrapper.dll";
HINSTANCE hInstance = (HINSTANCE)SafeLoadLibrary(DLL_FileName.w_str());
throw(Exception("Error loading DLL"));
Load = (Load_Ptr)GetProcAddress(hInstance,"Load");
int32_t Length = 1000;
int32_t RetVal = Load("", Array, &Length); // <-- path can be omitted as it is hard coded in DLL
So....I finally got it up and running, thanks to z32a7ul's proposal to set the 2nd Parameter to NULL and Remy Lebeau's advice of doing a check on the manually edited method. It seems, that this edited method itself is being called by the Load function. It was my fault in thinking of a missing overload where it was just needed to change a parameter def to a pointer type. Finally, I had to set the second param to NULL as mentioned above, but indirectly:
BSTR Path = SysAllocString(L"C:\\temp\\file.xml"); BSTR Schema = NULL; TC_MyClass *MyClass = new TC_MyClass(this); MyClass->Load((BSTR*)&Path, (BSTR*)&Schema);
Thanks for your answers.