Jump to content
  • Advertisement
Sign in to follow this  
mynameisnafe

C++ Multi-Byte - prepare string of extensions for OpenFileDialog

This topic is 1504 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

HI all,

 

I'm having some trouble figuring out how to do this one.

Assimp gives me a string, containing all the file extensions it can handle.

I found some code to tokenize strings on SO and wrapped it in a function.

The format of the string that is used to initialise the accepted extensions list for the OPENFILENAME structure is as follows:
 

L"All\0*.*\0Text\*.txt\0"

And so I'm trying to recreate that programatically. Which is a nightmare, as I'm trying to stick '\0' in the middle of a string.

// Get a vector of extensions supported by Assimp ( assimp exts string, tokenise by ';' )
std::vector<std::string> exts = Scatterbrain::Split(extsAccepted.c_str(), ';');
	
//create a string with a bunch of '\0' s in it, if possible
std::string extsList="";
for(size_t i = 0; i < exts.size(); i++)
	extsList.append(exts[i] + '\0');
	
// contains the first extension.
CString pwszExts(extsList.c_str());

// Initialize OPENFILENAME
ZeroMemory(&ofn, sizeof(ofn));

ofn.lpstrFilter = (LPCWSTR)pwszExts; // This one
...

// Display the Open dialog box. 
if( GetOpenFileName(&ofn) == TRUE ) 
{
...

Okay - I see one bug already: the dialog is going to say 'extension_a extension_b', instead of 'ext_name_a ext_a', but I'm happy to add blank spaces for names.

how is the generation of a string 

L"Hello\0World\0How\0Are\0You\0'

 done?

Thanks in advance

 

Share this post


Link to post
Share on other sites
Advertisement

how is the generation of a string

L"Hello\0World\0How\0Are\0You\0'

done?

Well, in your case, it would just be replacing ';' with '\0'.

So, maybe something like this:
std::wstring ext;
ext.reserve(str.length()+1);

std::transform(str.begin(),str.end(),back_inserter(ext),[](char t) -> wchar_t {
	return t == ';' ? 0 : t;
});
ext.push_back(0); // (This is necessary to indicate where the array of strings end)

wchar_t* buf = ext.c_str(); // An extra null terminator should be appended here.

// str = "Hello;World;How;Are;You"
// buf = L"Hello\0World\0How\0Are\0You\0\0"
EDIT:
Code sample here: http://ideone.com/wqrckP Edited by fastcall22

Share this post


Link to post
Share on other sites

What is this lambda magic?
 

[](char t)->wchar_t

Is that it? It's not the replacing that I'm struggling with, it's the char ->wchar_t.. and there is the char->wchar_t

I'm going to try basically an empty lambda, meanwhile, whats with the extra push_back(0)'s? are those important (i.e. the real null terminator of the returned exts wstring?

 

Share this post


Link to post
Share on other sites
According to the documentation for OPENFILENAME, lpstrFilter must be terminated by two NULL characters, as I had suspected. However, according to std::string::c_str, there should already be a null terminator appended to its contents. Therefore, the second push_back(0) is not needed. I will modify my previous post.

As for the lambda, [](char t)->wchar_t{}, it reads as: A lambda that takes one parameter, captures no variables in its parent scope(s), and returns a wchar_t. The equivalent C++03 code would look something like:
struct ReplaceAndWiden {
	char to_find;
	wchar_t to_replace;
	
	ReplaceAndWiden(char f, wchar_t r) : to_find(f), to_replace(r) {
	}

	wchar_t operator() (char t) const {
		return t == to_find ? to_replace : (wchar_t)t;
	}
};

std::transform( str.begin(), str.end(), back_inserter(ext), ReplaceAndWiden(';',0) );

Share this post


Link to post
Share on other sites

I bailed on my split fn, all that CString jazz.. now I have this beautiful code:


std::string App::DoOpenFileDialog(std::string extsAccepted)
{
	std::string fName;
	OPENFILENAME ofn;       // common dialog box structure
	char szFile[260];       // buffer for file name

	std::wstring wextsList;
	std::transform(extsAccepted.begin(), extsAccepted.end(), back_inserter(wextsList), [](char t)->wchar_t {
		return t==';' ? '\0' : t;
	});

	// Initialize OPENFILENAME
	ZeroMemory(&ofn, sizeof(ofn));
	ofn.lStructSize = sizeof(ofn);
	ofn.hwndOwner = m_hWnd;
	ofn.lpstrFile = (LPWSTR)szFile;
	ofn.lpstrFile[0] = '\0';
	ofn.nMaxFile = sizeof(szFile);
	ofn.lpstrFilter = (LPCWSTR)wextsList.c_str();
	ofn.nFilterIndex = 1;
	ofn.lpstrFileTitle = NULL;
	ofn.nMaxFileTitle = 0;
	ofn.lpstrInitialDir = NULL;
	ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;

	// Display the Open dialog box. 

	if( GetOpenFileName(&ofn) == TRUE ) 
	{	

..Problem is, it's missing every other file format - this is something I saw before while straying through the realms of doing it a char at a time in a for loop, and also while using a CString..

PS: D'oh! I've got Unicode set in my project settings (VS 2012), so that should make things easier?
PPS: I just saw your reply - I've used lambdas before, just appreciating the sly-as-hell widening, just implicit. Must use lambdas much more.Loving that one, using it, thank you smile.png

Crap - I just added the extra null terminator.

 

Edited by mynameisnafe

Share this post


Link to post
Share on other sites
std::transform(str.begin(),str.end(),back_inserter(ext),[](char t) -> wchar_t { return t == ';' ? 0 : t; });

I don't think that's actually going to do the right thing if there's anything other than US ASCII in the original string. A simple integral promotion from a multibyte UTF-8 string into Microsoft's proprietary UCS-2 wide-character Unicode variant is a fail.

Of course, if you're restricting your somain to US ASCII, you're fine.

Share this post


Link to post
Share on other sites

Okay..

 

Everything seems to be working with the lambda. The reason I have half the file formats visible in the dialog is to do with the OPENFILENAME and the GetOpenFileName() function

 

http://msdn.microsoft.com/en-gb/library/windows/desktop/ms646829(v=vs.85).aspx#open_file

 

I think I need a name for each format.. "Model" will do.

Hello people above smile.png std::string seems to work fine - I think (it appears) Assimp provides an overload - pretty sure I've seen it documented.


std::string GLFactory::GetAssimpExtsList()
{
std::string exts;
Assimp::Importer importer;
importer.GetExtensionList(exts);
return exts;
}

 

 

I don't think that's actually going to do the right thing if there's anything other than US ASCII in the original string.

 

Indeed. I'm happy to assume Assimp don't bother with language packs. PS That all sounds like a lot of effort - have you seen that lambda?! ohmy.png

Edited by mynameisnafe

Share this post


Link to post
Share on other sites

Bregma is correct, Assimp's aiString is UTF-8. Therefore, a temporary buffer and mbstowcs are needed for proper UTF-8 handling.

 

That will probably not be a correct conversion either, as mbstowcs expects the multibyte string to be in the system native multibyte encoding, which on Windows probably never is UTF-8.

 

You probably want something like this for a correct conversion:

#include <codecvt>
...
std::wstring_convert<std::codecvt_utf8<wchar_t>> wc;
std::wstring converted_str = wc.from_bytes(utf8str);

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!