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

I have got a library which was made for a desktop windows project. It is done in MFC VC++ by somebody else, and it works correctly. I will use one particular function from the library as an example for explaining the situation.
The example function goes like this:
CString GetFulPath(); // .h file
In the cpp file,
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;
}
Now, I can include this library in my project and do something like:
CwFolderBrowser cFolderBrowser;
if(cFolderBrowser.ShowDialog() == TRUE)
cPath = cFolderBrowser.GetFullPath();
This will show a folder browser dialog and let me choose a folder. It works fine on desktop windows.
Currently, I am working on Windows CE device. We have converted the library for use with Windows CE by removing unsupported functions and stuff. The library compiles and builds correctly without errors.
Next, I create an MFC Smart Device project, include the converted library, its h file and lib files and set the proper directories for dlls. The project builds fine. I can #include the library's h file properly too.
The problem arises as soon as I call the GetFullPath function:
cPath = cFolderBrowser.GetFullPath();
It gives me an unresolved external link error! The Intellisense does show this function in its list and I can choose it and everything. But in vain.
Strangely, If I modify the library and change GetFullPath()'s signature as below,
LPCTSTR GetFulPath(); // .h file LPCTSTR instead of CString
In the cpp file,
LPCTSTR CwFolderBrowser::GetFullPath() // Return type changed to LPCTSTR
{ // instead of CString
... // Body modified accordingly
}
then, the unresolved external Link error disappears and it works!
I am stumped about this strange behaviour, because, I can use CString normally in the MFC Smart Device project and there are no errors. The link error shows up only when I try to call functions from the library (and other such libraries) dll. At the same time, BOOL, int etc. seems to have no problems as function return types.
Ofcourse, I can go through each library and change every instanceof a CString return to LPCTSTR, but that would be a very big change. I would like to know why CString works fine in project as well as dll when on desktop, while, on Win CE, it works in the project but not i the DLL (At the same time, the DLL itself compiles fine without errors wether it uses CString or LPCTSTR!).
So, basically, I would like to keep the function CString if possible, and would like to know the reason why this happens. The exact same error also happenes in other libraries too.
Any help is appreciated. Thank you.
UPDATE:
I saw a page on ATL & MFC 7.0 which said about using the /Zc:wchat_t option. I have checked the Dll project as well as my application. Both use the same option of 'Treat wchar_t as Built-in type' as Yes. So, that option matches up.
Further, as I mentioned above, changing the function return to LPCTSTR works. The error disappears. Everything is going fine until I convert the returned LPCTSTR back to CString. The CString turns out as empty/Null. This happens both inside the dll code itself, as well as in my application code too.
UPDATE2:
Thanks to Michael and Cody, I changed the function to LPCTSTR and made sure that the values were not going out of scope before I could use them like they suggested. Now the empty/Null problem is solved and I can get the path values properly.
The problem that remains is that I have to convert all the CString functions to LPCTSTR, which is not exactly feasible. I would like to keep the functions as CString.

This is a classic problem and has been asked often here on SO.
This cannot work:
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
Maybe in some cases the function appears to work because the memory of the deleted CString object has not yet been overwritten.
You should do this (no error treatment here for simplicity):
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);

Related

String^ to LPCTSTR in VC++2010 (Windows form application)

How to convert System::string^ in to LPCTSTR ?
As my requirement is to clone a file using function CopyFile, it works fine if i give Fix name (OldFile.jpg and LatestFile.jpg) to its parameters (below Code: Works Fine)
LPCTSTR in_f,out_f;
in_f = _T("d:\\Old.jpg");
out_f = _T("d:\\Latest.jpg");
CopyFile(in_f,out_f,false);
above code clone the Old.jpeg in to a Latest.jpg but when i trying to give name (Latest.jpg) which is coming out from some String it won't create file (below Code: NOT Working)
String^ Name = "Latest";
//------Capture Current Date & Time
DateTime datetime = DateTime::Now;
//-------Convert Date Timt in to String
Name = Name + String::Format("{0}",datetime);
Name = Name->Replace('/','-');
Name = Name->Replace(':','-');
Name = Name + ".jpg";
LPCTSTR in_f,out_f;
in_f = _T("d:\\Old.jpg");
out_f = (LPCTSTR)Name; //Trying to Assign Current Latest file Name With date Time here
CopyFile(in_f,out_f,false);
The Problem is CopyFile Took LPCTSTR type as an argument , where as i am giving a type System::string^, So suggest me how to convert this System::string^ in to LPCTSTR so that i can add the current date time with in the name of my file.
I am Using VC++2010 and Windows form Application
Standard warning: While it's certainly possible to write the main body of your application in C++/CLI, or even write the GUI in C++/CLI using WinForms, it is not recommended. C++/CLI is intended for interop scenarios: where C# or other .Net code needs to interface with unmanaged C++, C++/CLI can provide the translation between the two. For primary development, it is recommended to use C# with either WinForms or WPF if you want managed code, or C++ with MFC if you want unmanaged.
I'm not sure I agree with Hans's comment that TCHAR is obsolete, but doing an explicit conversion to a wide string and calling CopyFileW is a good option.
Also, one could go the other direction, and convert from unmanaged to managed strings, and use the .Net method to copy files, File::Copy(String^, String^, Boolean).
To convert to a LPCTSTR, I would use marshal_as. Because it's implemented with templates, the compiler will resolve your LPCTSTR to call either the LPCSTR or LPCWSTR version, as appropriate.
Microsoft doesn't have dedicated documentation pages for each templated version of marshal_as, but the Overview of Marshaling in C++ page is a good place to start.
My test program:
#include <msclr\marshal.h>
int main(array<System::String^>^ args)
{
String^ managedStr = "I came from managed land!\r\n";
// This controls the lifetime of the LPCTSTR that marshal_as returns.
// When this goes out of scope, the LPCTSTR will no longer be valid,
// so be aware of its lifetime.
msclr::interop::marshal_context context;
LPCTSTR unmanagedStr = context.marshal_as<LPCTSTR>(managedStr);
OutputDebugString(unmanagedStr);
return 0;
}
Result:
I came from managed land!
You need to append a \0 character at the end of the Name string, since CopyFile() expects zero-terminated strings.
EDIT: As LucasTrzesniewski has pointed out, pinning a .NET string automatically yields a zero-terminated character array. This is documented in the C# specification. See the comments below for more information.
Moreover, you have to "pin" the string in memory, so the garbage collector won't move it around. Then you can create a pointer to the first character of the string. C++/CLI provides some utility types and functions to do this. Here's an example:
pin_ptr<const WCHAR> psName = PtrToStringChars (Name)
PtrToStringChars() is an inline function declared in vcclr.h. psName should be assignable to a LPCTSTR parameter - if not, use a cast. Note that PtrToStringChars() doesn't work if the input string is a nullptr. You need to test explicitly for this case. (However, in your case, Name is certainly not a nullptr).
The String remains pinned until the variable psName gets out of scope. This happens after leaving the { ... } block in which it's declared, e.g. after leaving the current function. No explicit unpinning is needed.
You need to marshaling it to "const char *" and make a static_cast to LPCTSTR.
Take a look at this https://msdn.microsoft.com/en-us/library/bb384865.aspx

How to import platform-specific struct?

I've got a struct in a file that begins with this line:
// +build windows
Therefore it will only be built on Windows. However, the part of the application that initializes everything needs to check if it is running on Windows and if so, create an instance of the struct. I have no idea how to do this without breaking things on other platforms.
For example, if the file contains a function newWindowsSpecificThing() and I compile on Linux, the function won't exist because it is defined in a file that isn't being compiled. (And, of course, this will produce an error.)
How do I work around this dilemma?
I think your solution would be to have some method on your struct which is used on all platforms. Look at how the dir_*.go files work for the os package. The func (file *File) readdirnames(n int) (names []string, err error) is available on all platforms by providing it in dir_plan9.go, dir_unix.go and dir_windows.go.
For your problem, I'd take the same approach but with some generic method that does internal work. In your application logic you'd call that function and in your file_unix.go file you'd define that function to do nothing (empty body).
Somewhere you clearly have a function that calls newWindowsSpecificThing(). That should be in a Windows-specific file. If it were, then it wouldn't matter that it isn't available. The fact that you have something "check if it is running on Windows" suggests a if runtime.GOOS == "windows" statement somewhere. Rather than have that, move the entire if into a function that is defined in a Windows-specific file. You'll also need to define that function in a !windows file, which is fine.
As an example from my code, I have a function:
func Setup() *config {
var cfg *config
// setup portable parts of cfg
return PlatformSpecificSetup(cfg)
}
I then have a file marked // +build windows that defines PlatformSpecificSetup() one way, and another marked // +build !windows that defines it another. I never have to check runtime.GOOS and I never have to deal with undefined data types. The config struct itself is defined in those files, so it can have different fields for each platform (as long as they agree enough for Setup()). If I were being more careful, I could create a struct like:
type config struct {
// independent stuff
plat *platformConfig
}
And then just define platformConfig in each platform file, but in practice I've found that more trouble than it's worth.

Can`t Load C++/CLI DLL resources

I'm trying just to see resource names but nothing appears.
I've made and compiled a C++/CLI (Managed) DLL in Visual Studio 2010 and added some Resource files as a test (one icon and one bitmap). I've checked with PE Explorer and the resources definitely are there.
My simple code:
Assembly asm = Assembly.LoadFrom("C:\\test.dll");
String[] res = asm.GetManifestResourceNames();
I know that the DLL is loaded because when I debug i can see all the infos in the 'asm' variable. Also i can Import data (using MEF) from the DLL.
So, the DLL has the resources and the code IS loading the assembly for sure. But why my 'res' variable always returns empty string list?
EDIT:
I've created a C# Class Library (.dll) with a resource just for a test. Now it works!! But still in my C++/CLI DLL the resources do not appear. Somehow they are in the DLL but the code cant reach it (only in the C++ DLL). Maybe it would have something to do with managed/unmanaged code, but since i'm compiling it with CLR it does not seem to be the case. Any suggestions?
SOLUTION
I've got it! Just in case someone needs.
According to these topics:
Embedding resource in a C++/CLI project
and
http://bytes.com/topic/net/answers/571530-loading-markup-xamlreader-load-resource-file#post2240705
the problem is exactly the C++/CLI thing. You have to add it in Input item under Linker tab in Project Properties. Now it seems to work fine. Thanks
I have a similar problem and your question helps me to solve it.
my project platform is C++/CLI and my DLL platform is c#.
I want to pack DLL into my executive file, hence we should put DLL in the project resource file through below steps at first:
1.copy DLL in project path.
2.put DLL name(e.g. test.dll) in below place
properties->linker->input->Embeded Managed Resource File
then we should read and use embedded DLL:
Stream^ stream = Assembly::GetExecutingAssembly()->GetManifestResourceStream("test.dll");
array<unsigned char>^ dllRawBuffer = gcnew array<unsigned char>(stream->Length);
int res = stream->Read(dllRawBuffer, 0, stream->Length);
stream->Close();
Assembly^ dllAssembly = Assembly::Load(dllRawBuffer);
System::Type^ testclass = dllAssembly->GetType("TestNamespace.TestClass");
MethodInfo^ TestMethod = testclass->GetMethod("TestMethodName");
// Create an instance.
Object^ Testobj = Activator::CreateInstance(testclass);
// Execute the method.
array<Object^>^ params = gcnew array<Object^>(2);
params[0] = 2;
params[1] = 3;
Object^ result = TestMethod->Invoke(Testobj, params);
obviously, this solution only works for managed DLLs.

Attempt to initialize the CRT more than once

I am using VS2008 to port code from VC6. When I ran the new build app, I get this error "R6031 Attemp to initialize the CRT more than once. This indicates a bug in your application".
There are a total of 21 dlls that are involve in the build this one app. Some DLL has .c files in them and explicitly calls _CRT_INIT() in DllMain. code below:
BOOL APIENTRY DllMain (HANDLE hModule, DWORD dwReason, LPVOID lpReserved)
{
switch( dwReason)
{
case DLL_PROCESS_ATTACH:
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
if(!_CRT_INIT( hModule, dwReason, lpReserved))
return FALSE;
break;
}
return TRUE;
}
I am not sure how to fix this problem. Do I need to comment out the call to _CRT_INIT()?
Thanks in advance.
Yes, you should not need to call _CRT_INIT() explicitly. It's probably being called by one or another DLLMain.
See MSDN for details.
Edit
I think you have misread MSDN:
When building a DLL which uses any of
the C Run-time libraries, in order to
ensure that the CRT is properly
initialized, either
the initialization function must be named DllMain() and the entry point
must be specified with the linker
option -entry:_DllMainCRTStartup#12 -
or -
You have named the init function DllMain(), so _CRT_INIT() is being called automatically. I think.
Why not simply comment out that line and see what happens?
This error code is specific to mixed-mode assemblies. Have you enabled the CLR during the port by mistake? You should not see this during a simple port from VC6 to a later Visual C++ revision.
This diagnostic indicates that MSIL
instructions were executing during
loader lock. For more information, see
Initialization of Mixed Assemblies.
You can check the project setting by right-clicking the project in Solution Explorer, then under Properties look at Configuration Properties -> General -> Common Language Runtime Support

Stack issues when calling a DLL compiled with Visual C++ in GCC

I'm trying to call some functions in a DLL compiled with (I believe) Visual C++ from my program, which is compiled using GCC.
To call the functions in the DLL, I do a LoadLibrary() on the DLL, and then a GetProcAddress() to get the address of a particular function, which I then call. This function returns a list of pointers to the functions in the DLL I'm to call.
Well, when I try to call those functions, they don't work properly. I ran my program through a debugger, and it looks like the DLL library function is looking for one of the passed arguments at ebp+8, even though GCC has put it at ebp-24.
It looks definitely like a stack issue. What's more, when the GCC program function which calls the DLL function returns, my program crashes -- so something screwey is going on with the stack. Does anyone know what I need to do in order to fix this? I'm not able to access the DLL code.
Also: I tried putting __cdecl and __stdcall before the DLL function definition in my program's source file, but this changes nothing.
Looks like a calling convention problem. Make sure you're putting the calling convention tag in the right place. With GCC, it should look like this:
typedef int (__stdcall *MyFunctionType)(int arg1, const char *arg2);
MyFunctionType myFunction = (MyFunctionType)GetProcAddress(myModule, "MyFunction");
// check for errors...
int x = myFunction(3, "hello, world!");
[EDIT]
Looks like your problem has nothing to do with calling conventions (although getting them right is important). You're misusing BSTRs -- a BSTR is not a simple char* pointer. It's a pointer to a Unicode string (wchar_t*), and furthermore, there is a 4-byte length prefix hidden before the first characters of the string. See MSDN for full details. So, the call to SetLicense() should look like this:
BSTR User = SysAllocString(L""); // Not sure if you can use the same object here,
BSTR Key = SysAllocString(L""); // that depends on if SetLicense() modifies its
// arguments; using separate objects to be safe
// check for errors, although it's pretty unlikely
(textCapLib.sdk)->lpVtbl->SetLicense((textCapLib.sdk), User, Key);
SysFreeString(User); // Hopefully the SDK doesn't hang on to pointers to these
SysFreeString(Key); // strings; if it does, you may have to wait until later to
// free them

Resources