I have two projects, A and B
Project A is compiled with:
1. Standard Windows Libraries
2. Multi-byte character set
3. NO common language support
Project B is compiled with:
1. MFC as a dynamic dll
2. Multi-byte character set
3. WITH using common language support
The following function is defined in project A:
LPCTSTR CAppProxy::DriverName(IDriverAgent *driver)
{
BSTR bstr;
HRESULT hr = driver->get_Name(&bstr);
CString str = CString(bstr);
return(str);
}
and is called from project B using:
CString name = appProxy->DriverName(driver);
but when driver name is called, the contents of "name" are garbage
A classic mistake, you're returning a pointer to a local object which reaches the end of its lifetime at the end of the function. The stack containing the string is released and overwriten with random stuff.
More specifically, you're returning a pointer to a data structure inside the CString object which is invalidated when the CString is destroyed.
If both projects are using the same version of CString you could return one of those instead of LPCTSTR.
Related
I am trying to use a DLL with an ANSI C compiler. One of the DLL functions takes a void pointer. In some sample Windows code that was provided with the DLL, the struct that gets passed to the function is defined as having three CString entities. I have told the author of the DLL that they should not be passing MFC classes through their DLL functions. They have told me just to replace the CString declarations in the struct with char arrays and it should be fine. I'm 99% sure that's wrong, but since I don't have VC++ and don't have any experience with MFC, and since I've seen some posts saying LPTSTR can be used in place of CString (What is `CString`?), I'm starting to wonder if I'm wrong.
Can someone please confirm for me that CString and LPTSTR are not interchangeable as arguments to a function? If you can provide the source for the definition of the CString class, that would be helpful so I can send it to the DLL's author and explain that the memory footprint of a char array is not the same as a CString class, and that you can't pass a pointer to a struct that was defined with char arrays and then treat it as a bunch of CString objects.
CString is an alias of the CStringT class template. Objects of this class are really better not to pass to the DLL. The character type of the string class can be TCHAR (for both ANSI and Unicode character strings - see explanation below). The definition of CString (and CStringT) can most likely be found in the atlstr.h header file.
LPTSTR is a regular pointer to a sequence of characters. The data type (TCHAR*) depends on the settings of the development environment: if the "Use Unicode Character set" option is selected, the TCHAR data type will be wchar_t (and LPTSTR will be wchar_t*, respectively). If the "Use Multi-byte character set" is selected, the TCHAR will be defined as char (and LPTSTR will be char*).
So the question about interchangeability between CString and LPTSTR is not so simple. It also depends on how the DLL is written. If the DLL was designed with the same environment settings as the main program, then CString and LPTSTR can really be interchangeable.
Also, remember that CStirng is a class with many methods, while LPTSTR is just a pointer.
I have been able to achieve a program that imports a C++ DLL function and uses it correctly to get the required calculated values. I'm returning the char* values to VB.net using an intptr pointer.
It works fine, however, I cant seem to rest or clear the memory space where the result from the function is stored. When i call the function the first time it gives me the right answers, when it is called the second time, it gives me both the first and the second answers.
Here are relevant parts of my code:
CPM.cpp - The function that calculated the return variables in the cpp file
char* CPMfn(char* sdatabase, int project_num)
{
/* Retrieve data from database and calculate CPM for the selected project number*/
char* testvector = getCPM(sdatabase, project_num);
return testvector;
}
CPM.h - the header file for exporting the function
#pragma once
#ifdef CPM_EXPORTS
#define CPM_API __declspec(dllexport)
#else
#define CPM_API __declspec(dllimport)
#endif
extern "C" CPM_API char* CPMfn(char*, int);
VB.net code to import the DLL, declare the function and use it
'' Import C++ CPM Calculation function from CPM DLL
<DllImport("CPM.dll", CallingConvention:=CallingConvention.Cdecl)>
Private Shared Function CPMfn(ByVal dbstring As Char(), ByVal task As Int32) As System.IntPtr
End Function
'' Get CPM results from DLL function with database location string and selected project number
CPMresults = CPMfn(DBString, Val(Project_IDTextBox.Text))
CPMvalues = Marshal.PtrToStringAnsi(CPMresults)
If CPMvalues.Length() = 0 Then
MsgBox("No tasks for seleccted project")
Else
MsgBox(CPMvalues) ' Show CPM values
End If
As I run it consecutively the string just keeps getting longer i.e. 4th function call would return the values for project 1, 2, 3 and 4.
I've checked online for the past few hours trying to figure out how to return char* from C++ DLLs and then how to clear the intptr.
I just haven't had any luck with the solutions suggested. I would really really appreciate some help.
Thank you!
Per the following MSDN documentation:
Default Marshaling Behavior
The interop marshaler always attempts to free memory allocated by unmanaged code. This behavior complies with COM memory management rules, but differs from the rules that govern native C++.
Confusion can arise if you anticipate native C++ behavior (no memory freeing) when using platform invoke, which automatically frees memory for pointers. For example, calling the following unmanaged method from a C++ DLL does not automatically free any memory.
Unmanaged signature
BSTR MethodOne (BSTR b) {
return b;
}
However, if you define the method as a platform invoke prototype, replace each BSTR type with a String type, and call MethodOne, the common language runtime attempts to free b twice. You can change the marshaling behavior by using IntPtr types rather than String types.
The runtime always uses the CoTaskMemFree method to free memory. If the memory you are working with was not allocated with the CoTaskMemAlloc method, you must use an IntPtr and free the memory manually using the appropriate method. Similarly, you can avoid automatic memory freeing in situations where memory should never be freed, such as when using the GetCommandLine function from Kernel32.dll, which returns a pointer to kernel memory. For details on manually freeing memory, see the Buffers Sample.
So, the DLL needs to dynamically allocate a new char* string each time it returns, and the VB code needs to be told how to free that string properly. There are a few ways to handle this:
have the DLL return a char* (or wchar_t*) string that is allocated with CoTaskMemAlloc(), and then change the PInvoke to take the return value as a string marshaled as an UnmanagedType.LPStr (or UnmanagedType.LPWStr). The .NET runtime will then free the memory for you using CoTaskMemFree().
change the DLL to return a COM BSTR string that is allocated with SysAllocString(), and then change the PInvoke to take the return value as a string marshaled as an UnmanagedType.BStr. The .NET runtime will then free the memory for you using SysFreeString().
if you want to have the DLL return a raw char* (or wchar_t*) string and have PInvoke treat it as an IntPtr (because it is not allocated using CoTaskMemAlloc() to SysAllocString()), then the .NET runtime will have no way of knowing how the string was allocated and so cannot free the memory automatically. So either:
the IntPtr will have to be passed back to the DLL when done being used, since only the DLL will know how the memory was allocated, so only the DLL will be able to free it properly.
have the DLL allocate the char* (or wchar_t*) string using LocalAlloc(), and then the .NET code can use Marshal.PtrToStringAnsi() (or Marshal.PtrToStringUni()) to get a string from the IntPtr, and then pass the IntPtr to Marshal.FreeHGlobal() when done using it.
See the following article for more details (it is written for C#, but you can adapt it for VB.NET):
Returning Strings from a C++ API to C#
Thanks a lot #Remy Lebeau. I've tried implementing the CoTaskMemAlloc() method and it works!. My code is as follows:
In the cpp file i've edited the return value to be allocated using the CoTaskMemAlloc()
char* CPMfn(char* sdatabase, int project_num)
{
/* Retrieve data from database and calculate CPM for the selected project number*/
char* CPMvector = getCPM(sdatabase, project_num);
/* Store results in specially allocated memory space that can easily be deallocated when this DLL is called*/
ULONG ulSize = strlen(CPMvector) + sizeof(char);
char* ReturnValue = NULL;
ReturnValue = (char*)::CoTaskMemAlloc(ulSize);
// Copy the contents of CPMvector
// to the memory pointed to by ReturnValue.
int charlen = strlen(CPMvector);
strcpy_s(ReturnValue, charlen + 1, CPMvector);
// Return
return ReturnValue;
}
In the VB.net file I've written the Dllimport and Marshalling code as follows:
'' Import C++ CPM Calculation function from CPM DLL
<DllImport("CPM.dll", CallingConvention:=CallingConvention.Cdecl, CharSet:=CharSet.Ansi)>
Private Shared Function CPMfn(ByVal dbstring As String, ByVal task As Int32) As <MarshalAs(UnmanagedType.LPStr)> String
End Function
'' Get CPM results from DLL function
Dim teststring As String = CPMfn(cDBString, Val(Project_IDTextBox.Text))
Alternatively I've also tried freeing up the allocated memory manually using the GlobalAlloc() and Marshal.FreeHGlobal() functions but i'm getting the same results (i.e. first call = 1,2,3\n, second call = 1,2,3\n,4,5,6\n instead of just 4,5,6\n).
Here's my code with the GlobalAlloc() method:
In .cpp file
ReturnValue = (char*)::GlobalAlloc(GMEM_FIXED, ulSize);
strcpy_s(ReturnValue, strlen(CPMvector) + 1, CPMvector);
And in VB.net file
<DllImport("CPM.dll", CallingConvention:=CallingConvention.Cdecl)>
Private Shared Function CPMfn(ByVal dbstring As Char(), ByVal task As Int32) As System.IntPtr
End Function
Dim CPMresults As IntPtr = CPMfn(cDBString, Val(Project_IDTextBox.Text))
Dim CPMvalues As String = Marshal.PtrToStringAnsi(CPMresults)
Marshal.FreeHGlobal(CPMresults)
CPMresults = IntPtr.Zero
Thanks for all the help so far!
i am working on making my applications international. After two days digging on msdn i came up with a test, which loads language-specific library containing resources. This is also my first attempt at loading library as a resource, loading strings from it and so on.
Next, according to msdn example at http://msdn.microsoft.com/en-us/library/windows/desktop/dd319071%28v=VS.85%29.aspx, i'm trying the LoadString.
Since string loading for entire application equals a lot of text copying, i thought i would use the - what i think is - memory efficient feature of LoadString, which is setting nBufferMax parameter to zero. According LoadString documentation, it should return a pointer to string resource. I thought i'd make a struct or a class of string pointers and do something along these lines (i extracted only the important bits):
wchar_t textBuf[SOMEVALUE]; // <-- this is how id DOES work
wchar_t *myString; // <-- this is how i would like it
HMODULE resContainer=LoadLibraryEx(L"MUILibENU.dll",NULL, LOAD_LIBRARY_AS_DATAFILE);
if(0!=resContainer){
// this works OK
int copied=LoadStringW(resContainer,IDS_APP_TITLE,textBuf,SOMEVALUE);
// this fails, also gives a warning at compile time about uninitialized variable used.
int copied=LoadStringW(resContainer,IDS_APP_TITLE,myString,0);
}
As you can see i am trying to get myString to become a pointer to loaded resource library's string without actually copying anything.
My question is: am i misunderstanding msdn documentation? Can i or can i not get a pointer to the string directly within loaded library, and simply use it later, e.g. to show a messagebox, without actually copying anything? Until i unload said library?
MSDN says:
[...] If this parameter is 0, then lpBuffer receives a read-only pointer to the resource itself.
It means that a) the pointer must be of type const wchar_t*:
const wchar_t *myString;
and b) you must pass a pointer to the pointer and use an ugly cast:
int copied=LoadStringW(resContainer,IDS_APP_TITLE,(LPWSTR)&myString,0);
I have C# project "X" ,I have exposed methods in it to the C++ project "Y".
X has method signature as follows -
public void WriteInformation(string sInfo)
{
m_logger.ErrorInfo("{0}", sInfo);
}
As I am exporting it to a C++ using .TLB file I checked declaration of this method in .tlh file which generates on #import of .tlb file.
virtual HRESULT __stdcall WriteInformation ( /*[in]*/ BSTR sMsg ) = 0;
I am calling this method in C++ project and passsing argument as follows -
oLog->WriteInformation(BSTR("Info write successful"));
Issue here is the string passed from C++ always becomes garbage or null , I debugged it and I can see value of sInfo is always garbage or null.
Please let me know what method should be followed to pass string from C++ to C#.
You try to pass an ANSI string in place of BSTR. BSTR must be a wide character string. Also you shouldn't pass a string literal, you should properly allocate a BSTR using SysAllocString() (or better yet) a wrapper class like ATL::CComBSTR or _bstr_t. Btw _bstr_t has a constructor that will accept const char* and do the ANSI->UTF16 conversion for you.
I dont think its possible to interact with C++ and C# directly. I had interacted using a C++/CLI wrapper.
I have this code in my VB6 app:
Private Declare Function FileGetParentFolder Lib "Z-FileIO.dll" _
(ByVal path As String) As String
Output.AddItem FileGetParentFolder(FileText.Text)
Output is a list, FileText is a text field containing a file path. My C++ DLL contains this function:
extern "C" BSTR ZFILEIO_API FileGetParentFolder(Path p)
{
try {
return SysAllocString(boost::filesystem::path(p).parent_path().c_str());
} catch (...) {
return SysAllocString(L"");
}
}
where Path is typedef'd as LPCSTR. The argument comes into my DLL perfectly, but whatever I try to pass back, the VB6 app shows only garbage. I tried several different methods with SysAllocStringByteLength, casting the SysAllocString argument to LPCWSTR and other variants. Either, I only see the first letter of the string, or I see only Y's with dots, just not the real string. Does anyone know what the real method is for creating and passing valid BSTRs from C++ to VB6?
Hopefully this will point you in the right direction. From memory...
VB6 uses COM BSTRs (2-byte wide character strings) internally, but when communicating with external DLLs it uses single- or multi-byte strings. (Probably UTF-8, but I don't remember for sure.) Your Path typedef to LPCSTR is an ANSI string, and that's why you can receive it correctly. The return value you generate is a wide-character string, but VB is expecting an ANSI string. You'll need to use WideCharToMultiByte to convert your return value before returning it.
Seems a little odd that VB does this implicit conversion, but that's the way it is. (As far as I remember.)
If you insist on using the function signature then you have to prepare a custom typelib for VB6 that includes this
[dllname("Z-FileIO.dll")]
module ZFileIO
{
[entry("FileGetParentFolder")]
BSTR FileGetParentFolder ([in] LPWSTR path);
};
In Declares param-types As String are automagically converted to ANSI string, i.e. LPSTR. The only way to pass/receive a unicode string (LPWSTR or BSTR) is by using typelib API function declaration.
Other than that you can always use As Long params in the declaration and expect LPWSTRs but then the consumer will have to wrap strings in StrPtr on every call to the API function.