I am trying to to create a Task Dialog, using the TASKDIALOGCONFIG strucutre. My application uses Unicode. This is my code:
string error_text = get_error_text();
string error_code = get_error_code();
TASKDIALOGCONFIG tdc = { sizeof(TASKDIALOGCONFIG) };
tdc.dwCommonButtons = TDCBF_OK_BUTTON;
tdc.pszMainIcon = TD_ERROR_ICON;
tdc.pszWindowTitle = _T("Error");
tdc.pszContent = error_text.c_str(); /* of course this will give a
const char* instead of a wchar_t* */
tdc.pszExpandedInformation = error_code.c_str(); // here is the same thing
tdc.hwndParent = m_wndParent;
TaskDialogIndirect(&tdc, NULL, NULL, NULL);
I have researched the problem a bit, but I haven't found a solution yet. Could anybody help me?
You have two options:
Use ANSI text. Do so by using TASKDIALOGCONFIGA and TaskDialogIndirectA.
Use Unicode text. Switch your strings from std::string to std::wstring.
I personally would recommend the latter option.
I would also recommend that you do not use tchar.h, and stop using _T(...). Since you are only targeting Unicode, you should write L"Error" rather than _T("Error"). It only makes sense to use tchar.h if you are writing code that must compile for both MBCS and Unicode targets. That was a necessary evil in the days when we needed to compile for Win 95/98 and Win NT/2000. But those days are long gone.
Related
I am using InstallShield 2013 Premium. I created a C++ dll in Visual Studio 2010 to provide some functionality I could not achieve with InstallScript alone. My C++ function needs to return a small string (a username) to the InstallScript after doing considerable work to get this value.
Throughout the C++ am I using CStringW to represent my strings. Ideally, I would like to return it as Unicode, but I'm content with ANSI if that's my only option. I have tried numerous approaches with CStringW, std::wstring, std::string, LPCTSTR, LPSTR, char *... I tried direct returns, and attempts to return by reference. Nothing works!
Sometimes the dll function hangs, sometimes it throws an exception, at best it returns garbage values with non-printing characters. The official documentation on this does not seem accurate (it doesn't work for me!). Extensive Googling, and searching the Flexera boards produce "solutions" from others struggling with the same ridiculous problem, and yet non of those work for me either...
I didn't try this until the end, as I took for granted that you could pass strings between dlls and InstallScript easily enough. In retrospect, I should have started with the interface between the two and then developed the dll functionality after that.
Thanks for the help guys! I finally figured this out for myself though. There are multiple facets to the solution, however, which I have not found documented or suggested elsewhere.
Major points
1) return a WCHAR * from C++
2) use WSTRING as the corresponding return type in the InstallScript prototype
3) return it into a regular STRING variable in InstallScript, and treat it like any other
4) retain the value the WCHAR * points to in the C++ dll in a static variable, otherwise it apparently gets deleted and the pointer becomes invalid
If you've gotten far enough to find yourself in the same boat, I probably don't need to serve up every detail, but here's a chunk of example code to help you along:
Visual Studio Def File
LIBRARY MyIsDllHelper
EXPORTS
getSomeStringW #1
C++ Header
#ifdef MYISDLLHELPER_EXPORTS
#define MYISDLLHELPER_API __declspec(dllexport)
#else
#define MYISDLLHELPER_API __declspec(dllimport)
#endif
#include <stdexcept>
#include <atlstr.h>
namespace MyIsDllHelper
{
class MyIsDllHelper
{
public:
static MYISDLLHELPER_API WCHAR * getSomeStringW();
};
}
C++ Source
#include "stdafx.h"
#include "MyIsDllHelper.h"
static CStringW someStringRetained;
CStringW getTheString()
{
CStringW s;
// do whatever...
return s;
}
WCHAR * MyIsDllHelper::MyIsDllHelper::getSomeStringW()
{
someStringRetained = getTheString();
return someStringRetained.GetBuffer( someStringRetained.GetLength() ) + L'\0';
}
InstallScript
#define HELPER_DLL_FILE_NAME "MyIsDllHelper.dll"
prototype WSTRING MyIsDllHelper.getSomeStringW();
function DoSomething( hMSI )
STRING svSomeString;
STRING svDllPath;
begin
// Find the .dll file path. (A custom function)
GetSupportFilePath( HELPER_DLL_FILE_NAME, TRUE, svDllPath );
// Load the .dll file into memory.
if( UseDLL( svDllPath ) != 0 ) then
MessageBox ("Could not load dll: " + svDllPath, SEVERE );
abort;
endif;
// Get the string from the dll
try
svSomeString = MyIsDllHelper.getSomeStringW();
catch
MessageBox( "Could not execute dll function: MyIsDllHelper.getSomeStringW", SEVERE );
abort;
endcatch;
// Remove the .dll file from memory.
if( UnUseDLL( svDllPath ) < 0 ) then
MessageBox ("Could not unload dll: " + svDllPath, SEVERE );
abort;
endif;
// Use the string
MessageBox( "svSomeString: [" + svSomeString + "]", INFORMATION );
end;
You're best off when you can make your interface use C approaches rather than C++ ones. Match the interface of functions like GetEnvironmentVariable in which your function accepts a pointer to a buffer (and for correctness a size of that buffer), and then writes into that buffer. The majority of your implementation shouldn't have to change, as long as you can finish with something like a StringCchCopy from your CString into the buffer.
Since you specifically mention CStringW and other Unicode string types, I'd suggest choosing LPWSTR (rather than LPTSTR) for the interface type.
Then all that's left is declaring this for consumption by InstallScript. This means the prototype should use WSTRING and BYREF. If the function interface is the same as GetEnvironmentVariableW, the prototype should look something like this:
prototype MyFunc(WSTRING, BYREF WSTRING, NUMBER);
You can use strings, but I guess the problem is with the encoding.
Have a look here: https://adventuresinscm.wordpress.com/2014/01/12/unicode-files-and-installshield/
I am trying to transform a QString to _bstr_t type as follows:
QString mFilename="C:/agatebo/amahoro.doc";
QByteArray srcBa1 = mFilename.toLocal8Bit();
const char *srcString1 = srcBa1.data();
CString myStringSrc(srcString1,srcBa1.size());
BSTR bstrUser = myStringSrc.AllocSysString();
_bstr_t user(bstrUser,TRUE);
but when I pass the _bstr_t I get to this function:
pdfObject->cPrintFile(user);
PDFCreator ,a program whose COM interface I am using just crashes .I suspect this has something to do with unicode but can’t figure out what yet.I should mention that when I directly pass a path to the file like this:
pdfObject->cPrintFile(L"c:\\agatebo\\amahoro.doc");
all is ok ,I simply want to be able to use QStrings that come from other modules of my Qt application.I am compiling with Qt 4.8 msvc2010 if this matters. I would appreciate any
You should be able to use QString.utf16()
void ProcedureThatTakesWChar(const wchar_t* s) {
std::wcout<<s<<L'\n';
}
void ProcedureThatTakesBstr(const BSTR& s) {
std::wcout<<s<<L'\n';
}
int main(int argc, char *argv[]) {
QString qs("this is a qstring");
//pass directly
ProcedureThatTakesWChar(qs.utf16());
//if you really want to use a _b_str initialise it with .utf16()
//_b_str will handle SysAllocString and SysFreeString. QString
//does not have to stay in scope.
{
_bstr_t bs(qs.utf16());
ProcedureThatTakesBstr(bs);
}
return 0;
}
Since you're on Windows and the method call works when you pass in a wide string, that means you're compiling in Unicode mode. This means that TCHAR,_bstr_t etc. are typedefed to wchar_t. You can learn more about Unicode support in Windows here.
The good news is that QString and Microsoft's Unicode implementation both use the same character encoding, UTF-16.
To get the raw QString data you simply call QString's utf16() method. This returns a pointer to an array of null-terminated unsigned shorts -- the same type as wchar_t on Windows!
So in all you should have to do here is this:
pdfObject->cPrintFile(mFilename.utf16());
I am new to _bstr_t's and still trying to get the hang of it. I was trying to check whether a particular string x is contained anywhere within the bstring. Something I would normally do like;
String x = "hello";
String example = "You! hello there";
...
if (example.find(x) != string::npos) {
...
Just for the record the intended platform is windows.
There is no need to use _bstr_t. Use the BSTR type.
Next, read Eric's Complete Guide to BSTR Semantics.
Lastly, you can use the BSTR in native code the way you would a normal character array in most cases.
BSTR bstr = SysAllocString(L"FooBarBazQux");
if (wcsstr(bstr, L"Bar") != NULL) {
// Found it! Do something.
} else {
// Not there.
}
SysFreeString(bstr);
MSDN for wcsstr.
Your example appears to be trying to use string::find from STL. But you specify your variables of type "String" (capitalized). If you instead did:
using namespace std;
string x = "hello";
string example = "You! hello there";
...
your example would compile. Do you actually have a BSTR or _bstr_t that you need to work with that you haven't shown? Even if so, it's pretty easy to make an std::string from a _bstr_t, and after that you can use STL as you normally would.
We're working on localizing our app and now have the chore of fixing all the hard coded strings to LoadString(), etc.
I have taken a quick look at this class but wonder if anyone has used any other good wrappers.
Some requirements/nice to haves:
terse replacements for the hardcoded strings - we don't want to add lines and lines of code.
free and royalty-free
EDIT
A little more information -
the hardcoded strings were all throughout the code. Sometimes they were converted by the compiler to CString, sometimes to std::string and sometimes just plain old char*.
We want to minimize the changes to the code base (250k+ lines of code - and would rather not do much to the places where all the strings are used as different types.
Thus the class/method must do multiple duty as wchar, CString, std::string, etc...
I think a class is overkill for this. I use this one:
inline const wchar_t * LoadResourceString(UINT resourceId)
{
wchar_t * buff;
int requiredLen = LoadStringW(
GetModuleHandle(0), // Replace this with your HINSTANCE if
// using a resource DLL of course :)
resourceId,
reinterpret_cast<LPWSTR>(&buff),
0);
if (requiredLen == 0)
{
THROW_LAST_WINDOWS_ERROR();
}
return buff;
}
EDIT: Of course you would need to put the HINSTANCE somewhere if you were intending to use this in a resource DLL. This assumes the resource strings are ecoded as part of a single binary.
Here's what I use:
extern HINSTANCE GetResourceInstance(); //Define elsewhere, or use a global hInst
template<int N>
class LoadStringRes
{
TCHAR tszString[N+1];
public:
LoadStringRes(int ID)
{
::LoadString(GetResourceInstance(), ID, tszString, sizeof(tszString)/sizeof(tszString[0]));
}
operator const TCHAR*() const
{
return tszString;
}
};
Use thusly:
MyFunctionThatNeedsAString(LoadStringRes<100>(IDS_HELLO));
It looks like a function call, but it's really an object construction/use/destruction/use.
The downside is that you have to know the ballpark length of the string. That's the price of the automatic/stack memory allocation.
Take a look at wxWidgets. It will increase your distributable setup file, but it is very powerful and provides lots of features (in addition to the one your're seeking) in a very elegant and efficient manner (yes, it's free and royalty-free).
(This is not so much a problem as an exercise in pedantry, so here goes.)
I've made a nice little program that is native to my linux OS, but I'm thinking it's useful enough to exist on my Windows machine too. Thus, I'd like to access Windows' environment variables, and MSDN cites an example like this:
const DWORD buff_size = 50;
LPTSTR buff = new TCHAR[buff_size];
const DWORD var_size = GetEnvironmentVariable("HOME",buff,buff_size);
if (var_size==0) { /* fine, some failure or no HOME */ }
else if (var_size>buff_size) {
// OK, so 50 isn't big enough.
if (buff) delete [] buff;
buff = new TCHAR[var_size];
const DWORD new_size = GetEnvironmentVariable("HOME",buff,var_size);
if (new_size==0 || new_size>var_size) { /* *Sigh* */ }
else { /* great, we're done */ }
}
else { /* in one go! */ }
This is not nearly as nice (to me) as using getenv and just checking for a null pointer. I'd also prefer not to dynamically allocate memory since I'm just trying to make the program run on Windows as well as on my linux OS, which means that this MS code has to play nicely with nix code. More specifically:
template <class T> // let the compiler sort out between char* and TCHAR*
inline bool get_home(T& val) { // return true if OK, false otherwise
#if defined (__linux) || (__unix)
val = getenv("HOME");
if (val) return true;
else return false;
#elif defined (WINDOWS) || defined (_WIN32) || defined (WIN32)
// something like the MS Code above
#else
// probably I'll just return false here.
#endif
}
So, I'd have to allocate on the heap universally or do a #ifdef in the calling functions to free the memory. Not very pretty.
Of course, I could have just allocated 'buff' on the stack in the first place, but then I'd have to create a new TCHAR[] if 'buff_size' was not large enough on my first call to GetEnvironmentVariable. Better, but what if I was a pedant and didn't want to go around creating superfluous arrays? Any ideas on something more aesthetically pleasing?
I'm not that knowledgeable, so would anyone begrudge me deliberately forcing GetEnvironmentVariable to fail in order to get a string size? Does anyone see a problem with:
const DWORD buff_size = GetEnvironmentVariable("HOME",0,0);
TCHAR buff[buff_size];
const DWORD ret = GetEnvironmentVariable("HOME",buff,buff_size);
// ...
Any other ideas or any suggestions? (Or corrections to glaring mistakes?)
UPDATE:
Lots of useful information below. I think the best bet for what I'm trying to do is to use a static char[] like:
inline const char* get_home(void) { // inline not required, but what the hell.
#if defined (__linux) || (__unix)
return getenv("HOME");
#elif defined (WINDOWS) || defined (WIN32) || defined (_WIN32)
static char buff[MAX_PATH];
const DWORD ret = GetEnvironmentVariableA("USERPROFILE",buff,MAX_PATH);
if (ret==0 || ret>MAX_PATH)
return 0;
else
return buff;
#else
return 0;
#endif
}
Perhaps it's not the most elegant way of doing it, but it's probably the easiest way to sync up what I want to do between *nix and Windows. (I'll also worry about Unicode support later.)
Thank you to everybody who has helped.
DWORD bufferSize = 65535; //Limit according to http://msdn.microsoft.com/en-us/library/ms683188.aspx
std::wstring buff;
buff.resize(bufferSize);
bufferSize = GetEnvironmentVariableW(L"Name", &buff[0], bufferSize);
if (!bufferSize)
//error
buff.resize(bufferSize);
Of course, if you want ASCII, replace wstring with string and GetEnvironmentVariableW with GetEnvironmentVariableA.
EDIT: You could also create getenv yourself. This works because
The same memory location may be used in subsequent calls to getenv, overwriting the previous content.
const char * WinGetEnv(const char * name)
{
const DWORD buffSize = 65535;
static char buffer[buffSize];
if (GetEnvironmentVariableA(name, buffer, buffSize))
{
return buffer;
}
else
{
return 0;
}
}
Of course, it would probably be a good idea to use the wide character versions of all of this if you want to maintain unicode support.
This wasn't the original question, but it might worth to add the MFC way to this thread for reference:
CString strComSpec;
if (strComSpec.GetEnvironmentVariable(_T("COMSPEC")))
{
//Do your stuff here
}
VC++ implements getenv in stdlib.h, see, for example, here.
The suggestion you made at the end of your post is the right way to do this - call once to get required buffer size and then again to actually get the data. Many of the Win32 APIs work this way, it's confusing at first but common.
One thing you could do is to pass in a best-guess buffer and its size on the first call, and only call again if that fails.
Don't bother. %HOME% is a path on Windows, and should be usable by all reasonable programs. Therefore, it will fit in a WCHAR[MAX_PATH]. You don't need to deal with the edge case where it's longer than that - if it's longer, most file functions will reject it anyway so you might as well fail early.
However, do not assume you can use a TCHAR[MAX_PATH] or a char[MAX_PATH]. You do not have control over the contents of %HOME%; it will contain the users name. If that's "André" (i.e. not ASCII) you must store %HOME% in a WCHAR[MAX_PATH].