I'm using VS 2012:
Here's my situation:
I'm writing some code in visual basic that calls a dll function that I've written in C++ (also written using vs 2012). One of my function's arguments takes the wchar_t* data type, so I need to format my string from vb accordingly using StrPtr.
Here's my problem:
Visual Basic does not recognize StrPtr as a function. I get: 'StrPtr' is not declared. It may be inaccessible due to its protection level.'
What does this mean?
StrPtr is a VB6 function, it does not exist in VB.Net
VarPtr, VarPtrArray, VarPtrStringArray, ObjPtr and StrPtr were
undocumented functions used to get the underlying memory address of
variables. These functions are not supported in Visual Basic .NET.
The equivalent .Net Framework method for wide strings is Marshal.StringToHGlobalUni().
Although likely you should be using P/Invoke to call a C++ funciton in which case the marshaling of a String to wchar_t is handled automatically.
This keyword has been removed from the language:
VarPtr, VarPtrArray, VarPtrStringArray, ObjPtr and StrPtr are
undocumented functions used to get the underlying memory address of
variables. These functions are not supported in Visual Basic 2008.
Related
I'm using an external library (Qpid Proton C++) in my Visual Studio project.
The API includes a method like:
container::connect(const std::string &url) {...}
I call it in my code this way:
container.connect("127.0.0.1");
but when debugging, stepping into the library's function, the string gets interpreted in the wrong way, with a size of some millions chars, and unintelligible content.
What could be the cause for this?
You need to put the breakpoint inside the function and not at the function declaration level, where the variable exists but is not yet initialized.
Just in case someone runs into a similar problem, as Alan Birtles was mentioning in his comment, one possible cause is having the library and your code using different C++ runtimes, and that turned out to be the case this time.
In general, as stated in this page from Visual C++ documentation,
If you're using CRT (C Runtime) or STL (Standard Template Library) types, don't pass them between binaries (including DLLs) that were compiled by using different versions of the compiler.
which is exactly what was going on.
While working on a C++/CLI project to wrap a native C++ DLL, I've come across a native function that takes in a std::string. Something like the following:
class NativeApi
{
public:
ErrorCode readFile(std::string filename = "path.csv");
};
Inside my managed wrapper implementation I allocate a new instance of the native class and call this function:
ref class ManagedApi
{
private:
NativeApi *api;
public:
ManagedApi(): api(new NativeApi()) { }
void Read()
{
api->readFile("apath.csv") // or with nothing to use default value
}
}
When I run this, I get the MDA PinvokeStackImbalance complaining that this call has unbalanced the stack. I was surprised, since the only other time I ever got this MDA was from C# when calling conventions didn't match. I never saw this happen with C++/CLI, where presumably all the matching is done automatically by the compiler.
Has anyone ever saw this before? Googling came up empty. I've looked at the DLL signature and it looks something like:
?readFile#NativeApi##QAE?AW4ErrorCode##V?$basic_string#DU?$char_traits#D#std##V?$allocator#D#2##std###Z
This tells me that the function is there, and takes as a sole argument a basic_string, which should match the standard std::string typedef.
No idea what could possibly have gone wrong. I can make other calls to the native API that do not involve strings perfectly fine.
It is likely that there's a difference between the definition of std::string that you're using vs. what was used to compile the native C++ DLL. Even if the definitions are the same, the native DLL probably isn't using the same version of the C runtime as you are, so when your DLL allocates memory for the std::string, the Native DLL will try to call delete on it (when the string is destroyed at the end of the readFile method), and that call to delete will go to a different heap than was used to allocate the object!
If you want to make this work, you'll have to use the exact same version of the compiler as was used on the native DLL. Note that you'll be limited to the Release build of your project, as you don't have a native DLL that was compiled with the debug runtime.
The proper fix to this problem is to use raw types when calling across DLL boundaries (in this case, wchar_t*). If you can request a change to the native DLL, I would do that. If only raw types are used, then there's no issue with using different runtimes, and everything works the way it should.
I have a DLL written in Delphi 7 that I need to use in Visual C++ 2008.
From documentation that came with DLL, I can see that function is declared as (Delphi 7):
function ReadInfo(pCOM, pBuf, pErr: Pointer):boolean;
where pCom is pointer to data structure:
TCOM = record
dwBaudRate: Longword;
nCom,
nErr,
nLang : Byte;
end;
pBuf is pointer to "array of byte" (as it is written in DLL's documentation).
pErr - not used.
So now in c++ (after successfully loading DLL with LoadLibrary), I call:
myFunc = (MY_FUNC_POINTER)GetProcAddress(dllHandle, "ReadInfo");
which also doesn't return any errors.
MY_FUNC_POINTER is defined as:
typedef bool (*MY_FUNC_POINTER)(TCOM*, BYTE*, void*);
where TCOM is:
struct TCOM
{
unsigned long dwBaudRate;
BYTE nComm;
BYTE nError;
BYTE nLanguage;
};
I defined:
TCOM MyCom;
BYTE *myRes;
myRes = new BYTE[1024*1024];
But after calling
myFunc(&MyCom, myRes, NULL)
I get “The value of ESP was not properly saved across a function call.” error.
There would appear to be a calling convention mismatch. On the face of it, the function declares no calling convention in the Delphi, so the default Borland register convention is used. Your C++ code does not declare a calling convention for the import so the default of cdecl is used. But it is plausible that the Delphi code and documentation are not aligned and the Delphi code actually uses a different calling convention. Check the Delphi code, or contact the vendor. No matter what, the error message that you report does indicate a binary mis-match across the boundary between your module and the other module.
If the function really does use the Borland register calling convention (but see below for more), then you cannot readily call the function from languages other than Delphi. In that case you'd need a bridge to adapt that to a standard calling convention such as stdcall. By that I mean a Delphi DLL that can call the original DLL and expose it's functionality a way suited to interop. A better solution would be to fix the root problem and build the DLL again using standard calling conventions.
In fact, I now suspect that all the other commentators are correct. I suspect that the Delphi documentation does not match the Delphi code. I suspect that the function really is stdcall. So you can, probably, solve your problem by changing the function pointer typedef to be as follows:
typedef bool (__stdcall *MY_FUNC_POINTER)(TCOM*, BYTE*, void*);
My reasoning for this is that in stdcall the callee is responsible for cleaning the stack. That's not the case for cdecl, and since all the parameters, and the return value, fit in registers, it's not the case for Delphi register calling convention, for this function. Since there is a stack pointer mis-match, it follows that the most likely explanation is that the Delphi function is stdcall.
All the same, it's not comfortable to be working out calling conventions this way. If you cannot get any help from the DLL vendor then I'd be inclined to dig a little deeper by looking at the DLL function's code under a disassembler.
I have a dll with exporting function
extern "C" __declspec(dllexport) IDriver * __stdcall GetDriver()
There is a programm which is written on Delphi. It can't see the function GetDriver().
It's double awful because I can not get and modify sources of this program.
What may be the reason of successful loading my dll (according log file) and failed call exporting function? Thank you.
Window 7 x64, Visual Studio 2010, C++ project for x86 target
The most likely explanation is that the function will have been exported with a decorated name. I'd expect it to have been exported with the name GetDriver#0. So you could import it like this:
function GetDriver: IDriver; stdcall; external 'DllName.dll' name 'GetDriver#0';
Use a tool like Dependency Walker to check the exact name used to export the function.
If you cannot modify the Delphi code, then you'll need to make your C++ DLL match. Do that by using a .def file which allows you control over the exported name.
The other problem you will face is that Delphi's ABI for return values differs from that used by most other tools on the Windows platform. Specifically a return value is semantically a var parameter. On the other hand, your C++ compiler will regard the return value as an out parameter. My question on Delphi WideString return values covers exactly this issue.
Because of this, I'd expect the function declaration above to lead to access violations. Instead you should declare the return value to be a Pointer and cast it to an interface reference in your Delphi code. You'll need to double check and make sure that the reference counting is handled appropriately.
Again, if you cannot modify the Delphi code, you need to make the C++ code match. A Delphi interface return value is implemented as an additional var parameter following the other parameters. So, to make your C++ function match, declare it like this:
void __stdcall GetDriver(IDriver* &retVal);
Is it possible to pass a Delphi stream (TStream descendant) to a DLL written in c/c++? DLL will be written in Microsoft c/c++. If that is not possible, how about if we use C++ Builder to create the DLL? Alternatively, are there any Stream (FIFO) classes which can be shared between Microsoft C/C++ and Delphi?
Thanks!
You can do this using IStream and TStreamAdapter. Here's a quick example (tested in D2007 and XE2):
uses
ActiveX;
procedure TForm1.DoSomething;
var
MemStream: TMemoryStream;
ExchangeStream: IStream;
begin
MemStream := TMemoryFile.Create;
try
MemStream.LoadFromFile('C:\Test\SomeFile.txt');
MemStream.Position := 0;
ExchangeStream := TStreamAdapter.Create(MemStream) as IStream;
// Pass ExchangeStream to C++ DLL here, and do whatever else
finally
MemStream.Free;
end;
end;
Just in case, if you need to go the other way (receiving an IStream from C/C++), you can use TOleStream to get from that IStream to a Delphi TStream.
Code compiled by Microsoft C/C++ cannot call methods directly on a Delphi object. You would have to wrap the methods up and present, to the C++ code, an interface, for example.
Code compiled by C++ Builder can call methods directly on a Delphi object.
In general, wrapping up a Delphi class and presenting it as an interface is not completely trivial. One reason why you can't just expose the raw methods via an interface is that the Delphi methods using the register calling convention which is proprietary to Embarcadero compilers. You'd need to use a calling convention that is understood by the Microsoft compiler, e.g. stdcall.
Another complication comes with exceptions. You would need to make sure that your interface methods did not throw exceptions since your C++ code can't be expected to catch them. One option would be to use Delphi's safecall calling convention. The safecall calling convention is stdcall but with an added twist that converts exceptions into HRESULT values.
All rather straight forward in concept, but probably requiring a certain amount of tedious boilerplate code.
Thankfully, in the case of TStream, you can use TStreamAdapter to expose the Delphi stream as a COM IStream. In fact, the source code for this small class shows how to handle the issues I describe above.