How to assign FString to LPCTSTR

I’m making use of Windows’ open file dialog as described here.

To specify file type filters, I have a dialog->Filter variable that represents the lpstrFilter on the link above. My input comes in as FString however. And I’m having trouble getting it to work.

bool UBlahBlah::ShowSaveFileDialog(FString Extension, FString ExtensionName) {
    //this is a sample blueprint function
	FileDialog* dialog = new FileDialog(); //dialog->Filter is of type LPCTSTR
	FString filter = TEXT("Custom File \0*.cfl\0\0"); // i will be concatenating Extension and ExtensionName. But just to illustrate
	dialog->Filter = filter.GetCharArray().GetData(); // this doesn't work even though it's the exact same string
	dialog->Filter = TEXT("Custom File \0*.cfl\0\0"); // this works
}

The TEXT macro apparently produces a different result than the FString’s GetCharArray().GetData(). I’m guessing it has to do with encoding because I end up with some Far Eastern characters in my filter dropdown. According to the Unreal docs, TEXT produces UTF8 characters, just like FString. So why does the FString lose its null terminators?

I’ve tried std::string and std::wstring. They also produce the same artifacts as FString. Could someone explain what’s going on and what I need to do?

I use FString because it’s a custom Blueprint function. I may be wrong, but I think it’s the only string type supported by blueprint functions. If that’s not the case, I’d gladly change to anything that works better.

FString also seems to work better since I have to do a bit of concatenation. seems impossible to do with char arrays. Something like this
FString filter(ExtensionName.Len() + 2 + Extension.Len(), *(ExtensionName + TEXT("\0") + Extension + TEXT("\0")));

I tried constructing the FString this way. It still terminates at the first \0
FString filter(20, TEXT("Custom File \0*.cfl\0"));

I don’t understand the part about the Copy function. Could you elaborate a bit on that?

FString’s assignment operator gets the length of the input string by calling strlen() so it only reads the first null and stops there.

First, you don’t have to use FString unless there’s a compelling reason. But if you have one then I only see one option.

There is a constructor in FString that takes a length and a string. It calls strncpy so you should be good. But it does add a null at the end so you probably don’t need two "\0"s.

I don’t see any Copy function, just the operator=().

OK then it looks like FString isn’t designed to handle more than one null terminator. I see two options.

You can keep the extension name and the filter in separate FStrings. Every time you show the dialog, you pull out their text and concatenate them into one string.

Or you could use a different separator in your FString (a character that won’t appear in the name/extension strings), then you can pull out the text, replace all instances of that character with a null, and pass it to the dialog box.

The second method looks like it could work. But at this point, I can’t get any string type at all to work. For example, even this wstring method fails
std::wstring filter(TEXT(“Khaliscope File \0*.khl\0\0”));
dialog->Filter = filter.c_str();
The only thing that seems to work is using TEXT directly. And that doesn’t allow me to concatenate or replace strings. If there was a way perhaps to use TEXT on a string type rather than a literal. Or if it could take a format string then I could do something like
TEXT("%s \0 %s \0", ExtensionName, Extension)

What string type does TEXT use internally? That seems to be able to hold more null terminators than FString, string or wstring.

Thanks to Jin_VE pointing out that it was a problem with having multiple null terminators, I was able to search in the right direction. This question helped me find the solution to my problem.
Using std::wstring, I did the following and it worked. No idea what push_back is doing differently, though.

std::wstring filter = *ExtensionName;
filter.push_back('\0');
filter.append(*Extension);
filter.push_back('\0');
dialog->Filter = filter.c_str();

You have to do it yourself because all the string functions assume a single null terminator. If you use a non-null character for the separator then you can just loop through all the characters in the string and replace each instance of your separator with the null character. Don’t rely on standard string functions because they are not going to work like that.

Thank you very much. I feel like I understand strings a lot better now. I’d been taking them for granted in other languages where they just magically worked.

Hmmm… I can’t manage to get this working and I don’t think I’m doing anything differently.

#include <string>

LPCWSTR GetFilters()
{
	FString ExtensionName;
	FString Extension;
	std::wstring FilterString;


	ExtensionName = "SamuraiFiles";
	Extension = "*.sam";

	FilterString = *ExtensionName;
	FilterString.push_back('\0');
	FilterString.append(*Extension);
	FilterString.push_back('\0');

	return FilterString.c_str();
}