x

Search in
Sort by:

Question Status:

Search help

  • Simple searches use one or more words. Separate the words with spaces (cat dog) to search cat,dog or both. Separate the words with plus signs (cat +dog) to search for items that may contain cat but must contain dog.
  • You can further refine your search on the search results page, where you can search by keywords, author, topic. These can be combined with each other. Examples
    • cat dog --matches anything with cat,dog or both
    • cat +dog --searches for cat +dog where dog is a mandatory term
    • cat -dog -- searches for cat excluding any result containing dog
    • [cats] —will restrict your search to results with topic named "cats"
    • [cats] [dogs] —will restrict your search to results with both topics, "cats", and "dogs"

Commandlets not marked with _API

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?

Product Version: UE 4.13
Tags:
more ▼

asked Feb 02 '17 at 07:41 AM in C++ Programming

avatar image

kirdaybov
83 1 3 10

avatar image Nitronoid Jul 10 '17 at 08:34 AM

I'm looking for the same thing, did you find a solution?

avatar image kirdaybov Jul 11 '17 at 07:03 AM

No, unfortunately. I copied the commandlet into my own codebase, renamed (and rewrote a bit) conflicting names, and called it where I wanted to.

avatar image MatzeOGH Jul 10 '17 at 11:32 AM

Can you post some example code? I don't quite understand what you want to archive.

I think you can create new ones and add them to the delegate that you need

avatar image Nitronoid Jul 10 '17 at 12:05 PM

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

avatar image kirdaybov Jul 11 '17 at 07:18 AM

Well, here's what I want to do:

 void TextGatherer::Gather()
 {
 #ifndef UE_4_15
   UGatherLocTextFromSourceCommandlet* Commandlet = NewObject<UGatherLocTextFromSourceCommandlet>(GetTransientPackage());
   Commandlet->AddToRoot();
 
   TSharedRef<FManifestInfo> CommandletManifestInfo = MakeShareable(new FManifestInfo());
   Commandlet->Initialize(CommandletManifestInfo, nullptr);
 
   if (Commandlet->Main("") == 0)
   {
     UELog_S(UGatherLocTextFromSourceCommandlet, Debug, log << "successful gathering");
 
     UTextLibrary* Library = UTextLibrary::StaticClass()->GetDefaultObject<UTextLibrary>();
     for (auto Entry : Commandlet->ParseCtxt.MyManifestInfo.MyEntries)//= CommandletManifestInfo->GetManifest()->GetEntriesBySourceTextIterator(); ManifestEntryIt; ++ManifestEntryIt)
     {
       Library->GetText(*Entry.Namespace, *Entry.Key, *Entry.Source);
     }
   }
 #endif // UE_4_15
 }

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?

(comments are locked)
10|2000 characters needed characters left
Viewable by all users

1 answer: sort voted first

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.

more ▼

answered Jul 10 '17 at 07:50 PM

avatar image

kamrann
2.1k 83 36 120

avatar image kirdaybov Jul 11 '17 at 07:47 AM

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 :)

avatar image Nitronoid Jul 11 '17 at 09:57 AM

I tried this however it fails to load the class, I also tried using ANY_PACKAGE instead of nullptr but this also fails, this is my exact code:

 UClass* Class = LoadClass< UCommandlet >(ANY_PACKAGE, TEXT("UImportAssetsCommandlet"));
     auto Cmdlet = NewObject< UCommandlet >(GetTransientPackage(), Class);
     Cmdlet->Main(TEXT("[-help]"));

I have tried without the U prefix to UImportAssetsCommandlet, without the Commandlet postfix and with nullptr but none of these are working.

avatar image kamrann Jul 11 '17 at 11:30 AM

Sorry my mistake, you need to specify full path like this:

 UClass* Class = LoadClass< UCommandlet >(nullptr, TEXT("/Script/UnrealEd.ImportAssetsCommandlet"));

Also I didn't mean to suggest you should actually put params inside [] when you call Main().

avatar image Nitronoid Jul 11 '17 at 11:52 AM

Thanks very much for your help, this worked exactly as I needed!

avatar image MatzeOGH Jul 11 '17 at 12:03 PM

oh wow. This is one hell of a hack

avatar image kamrann Jul 11 '17 at 02:32 PM

Haha. Yep, I didn't say I recommend doing it. ;)

avatar image Nitronoid Jul 11 '17 at 02:53 PM

Well is there another way to run multiple commandlets from one instance of the editor?

avatar image kamrann Jul 11 '17 at 03:18 PM

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.

avatar image Nitronoid Jul 11 '17 at 03:43 PM

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.

avatar image Nitronoid Jul 13 '17 at 09:45 AM

Ok I fixed this. Stupidly had isEditor set to false....

(comments are locked)
10|2000 characters needed characters left
Viewable by all users
Your answer
toggle preview:

Up to 5 attachments (including images) can be used with a maximum of 5.2 MB each and 5.2 MB total.

Follow this question

Once you sign in you will be able to subscribe for any updates here

Answers to this question