It is useful sometimes to run some of the existing commandlets from code. For example, now I’m trying to add extra logic to the localization pipeline. The problem is that I cannot use commandlets in my own modules, because they aren’t marked with UNREALED_API! Attempts to call
NewObject()
leads to unresolved external symbol. So, I either have to copy the whole thing into my module or I have to modify the UE4 engine code which is not encouraged in our team.
Is there a way I can execute commandlets in my modules and access their results without marking these commandlets with UNREALED_API in UE4 source code?
So I want to use a standard commandlet to collect source code texts and then do my own thing with. If you are curious about what exactly: our game is originally in Russian, but you can’t put Russian texts in source code (causes problems on Mac) and sometimes it’s necessary. We are forced to put English texts in code and so there’s a problem when you are extracting texts for localization, there are now English texts in Russian locale and you would need to double translate it.
The solution was to keep source texts in an object (which is also saved into an .ini file) but refer to it with the same macros LOCTEXT, NSLOCTEXT. And to exclude the need to put everything in that object manually why not use a handy commandlet that knows how to gather these texts from the code?
Yeah, I’ve seen this solution somewhere when I was trying to resolve this, but as you correctly mentioned it won’t give me much.
As for the second suggestion, hmm… I think I’ve tried it but with an Unreal Cast and had problems, but I didn’t think about a static cast. I’ll try that
All I’m really aiming to do is run two commandlets, one after the other, without needing to open and close an editor process for each. The method above does that, however I’m now receiving errors from the import assets commandlet the headline: Unhandled Exception: EXCEPTION_ACCESS_VIOLATION writing address 0x00001894
I guess it’s trying to save assets to a location it doesn’t have permission for?
If there is another way it would be great… seems very inefficient to have to run every commandlet on a separate process.
An example could be running one of the existing commandlets such as fixupredirects or importassets, from our own commandlets, however this is seemingly not possible as the existing commandlets haven’t been marked with _API
If you only need access to a base class interface of virtual functions (like UCommandlet in this case), then it is possible to be sneaky and create an instance of a non-exported class using the reflection system. For a class USomeCommandlet in module SomeModule, this should work:
UClass* Class = LoadClass< UCommandlet >(nullptr, TEXT("/Script/SomeModule.SomeCommandlet"));
auto Cmdlet = NewObject< UCommandlet >(GetTransientPackage(), Class);
Cmdlet->Main(TEXT("[params]"));
Keep in mind though that commandlets are intended to be run from the command line, so you probably don’t gain anything with this approach over just invoking a command line call.
Thinking about it, if you want to be really evil, you could use a static/C-style cast to cast Cmdlet above to the derived commandlet type, in which case you should be able to access its public data members and inline functions even if it’s not exported.
Well, you can execute them by just invoking an OS command line call (via FPlatformProcess). But not in a single process, no. They’re just not designed to be used that way. Likewise they’re not intended to have their member variables accessed from outside.
But if you need to for whatever reason, and the above workaround works, then fair enough. It’s better than just copying source code at least.