Exporting C++/CLI native class (C4679) - c++-cli

I have a public ref class in a C++/CLI assembly (MyAssembly) containing a public static method that accepts native parameters.
#pragma make_public(nativeTypeA)
namespace namespaceA
{
public ref class MyClass : namespaceB::MyClass
{
public:
static managedTypeA ^ MethodA(nativeTypeA param);
static managedTypeB ^ MethodB(nativeTypeB param);
}
}
I would like to expose this method to another C++/CLI assembly. The managed assembly compiles fine but the assembly that references it (CallingAssembly) generates the following warning for MethodB:
warning C4679: 'namespaceA::MyClass::MethodB' : could not import member
This diagnostic occurred while importing type 'namespaceA::MyClass ' from assembly 'MyAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'.
I cannot use make_public on nativeTypeB because it is a typedef of a templated class, however, I am using make_public for non-templated native types (e.g. nativeTypeA) and it works (i.e. no C4679 when compiling CallingAssembly). Instead of using make_public, I have declared the native class public and used __declspec(dllexport) in the native header via preprocessor directives as suggested in this post. It was also necessary to conditionally exclude the "public" modifier (via CLR_ACCESS_MODIFIER) as the class is also included in other native projects:
#ifdef MANAGED
#define CLR_ACCESS_MODIFIER public
#ifdef MYASSEMBLY_DEF
#define MYASSEMBLY_LINKAGE __declspec(dllexport)
#else
#define MYASSEMBLY_LINKAGE __declspec(dllimport)
#endif
#else
#define MYASSEMBLY_LINKAGE
#define CLR_ACCESS_MODIFIER
#endif
template<>
CLR_ACCESS_MODIFIER class MYASSEMBLY_LINKAGE nativeTypeB<TT> : public nativeTypeB_base<TT> {
...
}
I have also done this for the base class of nativeTypeB (necessary in order to compile), and its typedef:
typedef public nativeTypeB<TT> MYASSEMBLY_LINKAGE nativeTypeB;
I'm not sure whether the line above is necessary, but C4679 still occurs either way.
I've done the usual checks: the MANAGED preprocessor directive is defined both projects; MYASSEMBLY_DEF is defined in MyAssembly; and I have added a reference to MyAssembly in CallingAssembly as well as MyAssembly.lib to its link line. The project build order is correct and there are no missing dependencies but I am still getting C4679.
I could change the interface to accept non-templated types but I don't really want to do this as it will lead to code bloat and is less elegant. This post mentions that the normal use of "public" in my native class should work.
Can anyone help?
Thanks in advance!

Passing objects of native classes across DLL boundaries has never been a good idea, because it's so very easy to violate the One Definition Rule.
C++/CLI does nothing to help this, instead it provides the ability to generate managed types which are specifically designed for sharing across assemblies. It also prevents you from courting disaster (ODR violations) by sharing native types. You can use the make_public pragma to override this, but with restrictions (such as no templates).
The better way to share native types is via COM-style interfaces.

Related

Exclude Swift Class from Bridging Header

In a Swift project that has mixed Obj-C and C++, I have a class that extends the class belonging to a 3rd party framework. When I compile the project, the compiler complains that it Cannot find interface declaration for '<Framework Class Name>', superclass of '<My Subclass Name>'
The semantic error points to the auto-generated bridging header ('MyProjectName-Swift.h').
Is there a way to not have a particular class included in the bridging header besides marking it private or fileprivate? (Which makes the subclass not much use in the rest of the project..) I've looked through the Apple docs on this matter and there doesn't seem to be any specific direction on this.
Alternatively, any clues as to how to fix this? The header includes this bit:
#if __has_feature(modules)
#import UIKit;
#import Material;
#import CoreGraphics;
#import ObjectiveC;
#endif
Which seems like it should make the proper reference to the superclass (in this case, if it matters, Material.PresenterCard) But — I'm pretty sure this pre-compiler directive isn't being referred to as I've heard of a related bug.
I recently had a very similar problem and solved it by defining an empty Objective-C class for the case where modules weren't available.
My situation was App -> Framework -> Package (Swift Package)
Framework implemented DerivedClass, which subclassed BaseClass defined in Package
When importing Framework-Swift.h internally or externally within any Objective-C++ code, the compiler would complain that BaseClass could not be found in the generated header
I solved this by adding the following code to Framework's umbrella include (Framework.h):
#if !__has_feature(modules)
#interface BaseClass: NSObject
#end
#endif
So when modules are unavailable (Objective-C++), BaseClass is defined as an empty Objective-C class. This allowed the framework to be imported into Objective-C++ code to use other features of the framework

Adding a C++ Header to Objective-C project

I've got a strange error working with Photoshop connection API in OSx.
I need to include the header of a cpp file to my project... I start from the adobe example and I included the code in this way:
#include "PSCryptor.h"
which contains the PSCryptor class :
class PSCryptor
{
public:
...
As soon as i try to use PSCrypor object, like with this code
static PSCryptor *sPSCryptor = NULL;
I get this error:
Unknown type name 'class'; did you mean 'Class'?
Could you help me to understand which is my error?
The file is being included in Objective-C files — that is, they have the extension ".m" or they are specifically configured to be compiled as Objective-C (probably the former). Thus, the compiler tries to interpret the code as Objective-C, but C++ is not valid Objective-C, so it complains.
What you need to do is use Objective-C++ instead. Simple fix: change the extension of the files that use that header from ".m" to ".mm".

Objective-c obfuscation of methods works in DEBUG but crashes in RELEASE

I made a xcode project where i did some security stuff and they asked me to obfuscate the method names
like so
#define specialMethod a9328238
+(void) specialMethod
{
// do security stuff
}
i made a .framework library from the project ( project A ) and included it into another project ( project B ).
but when i run (project B) with a Release build configuration it always crashes like so.
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '+[SecurityClass a9328238]: unrecognized selector sent to class 0x337cc4'
so it crashes when it tries to acces the method.
But when i run (project B) it with a Debug build configuration it runs smooth
(i have kept all my build configuration settings as default)
Where have you placed the #define for obfuscation ? Is it in the header file (.h) or in the implementation file (.m) of the framework ?
For the obfuscation to be effective, it must be placed in a file that is both included by the implementation and the caller.
You can also check that the pre-processing is ok by inspecting the pre-processed file. Select the implementation file and go to the menu Product > Generate Output > Generate Preprocessed File (you can select the configuration at the bottom of the screen).
My hunch is the #define location/visibility as well.
But you may want to consider this from another angle. You could change:
#define specialMethod a9328238
+(void) specialMethod
{
// do security stuff
}
to:
#interface SecurityClass : NSObject
// private obfuscated interface:
+ (void)a9328238;
// {
// do security stuff in a9328238's definition
// }
#end
// here is the public interface:
static inline void SecurityClass_LogIn() {
[SecurityClass a9328238];
}
dropping #define altogether.
In use:
SecurityClass_LogIn();
…
Since this is a class method, you could write an obfuscated function wrapped in a human readable inline instead. A well crafted C implementation will be much more difficult to pick apart than objc.
A more complete example would help us narrow down the possibilities.
Also verify there are no warnings -- the compiler may warn you if you have called an undeclared selector. It's possible that the method is called where the #define is not visible in other cases.
It seems that the executable which imports the obfuscated framework tries to access the non-obfuscated methods.
You should check the symbols in the framework. Use nm on the static library in the framework to see the exported symbols (marked with a 't'). Make sure the symbols are obfuscated.
If you've wrapped everything into a framework, have you made sure that the appropriate headers are exposed outside of the framework? Headers inside a framework aren't exposed the same way as normal files are. Go to your Project->Build Phases, in the bottom right you should see "Add Copy Headers". This will add a new section in your build phases. Inside this section, click the "+" and the headers that define your method names.

Type visibilty for a header Share a header file shared between native and managed clients

I have a header file that is included by both a native cpp file and a managed cpp file(compiled with /clr). It includes only native types, but I want to specify that the native types are visible outside the assembly
(see http://msdn.microsoft.com/en-us/library/4dffacbw(VS.80).aspx).
Essentially, I want:
public class NativeClass // The public makes this visible outside the assembly.
{
};
If I include this code from a native cpp, I get the following error:
error C3381: 'NativeClass' : assembly access specifiers are only available in code compiled with a /clr option
Attempted solution:
I'm currently using a preprocessor solution that causes the public to appear when compiling with the managed client, but it does not appear for the native client:
#ifdef __cplusplus_cli
#define CLR_ASSEMBLY_ACCESS_SPECIFIER__Public public
#else
#define CLR_ASSEMBLY_ACCESS_SPECIFIER__Public
#endif
CLR_ASSEMBLY_ACCESS_SPECIFIER__Public
class NativeClass
{
};
Question:
Is this the appropriate way to achieve this, or is there a better way?
Have you tried the make_public pragma listed on the MSDN page you linked to?
Otherwise, the solution you have is perfectly valid. I'm curious to know why you want to export native types from a CLR assembly though.

Forward-declare enum in Objective-C

I'm having trouble with enum visibility in an Objective-C program. I have two header files, and one defines a typedef enum. Another file needs to use the typedef'd type.
In straight C, I would simply #include the other header file, but in Objective-C, it's recommended not to use #import between header files, instead using forward #class declarations as needed. However, I can't figure out how to forward-declare an enumeration type.
I don't need the actual enumerated values, except in the corresponding .m implementation file, where I can safely #import away. So how can I get the typedef enum to be recognized in the header?
Most recent way (Swift 3; May 2017) to forward declare the enum (NS_ENUM/NS_OPTION) in objective-c is to use the following:
// Forward declaration for XYZCharacterType in other header say XYZCharacter.h
typedef NS_ENUM(NSUInteger, XYZCharacterType);
// Enum declaration header: "XYZEnumType.h"
#ifndef XYZCharacterType_h
#define XYZCharacterType_h
typedef NS_ENUM(NSUInteger, XYZEnumType) {
XYZCharacterTypeNotSet,
XYZCharacterTypeAgent,
XYZCharacterTypeKiller,
};
#endif /* XYZCharacterType_h */`
The answer to your question is to either go ahead and import the typedef header file or to use a generic type like NSInteger instead of the enum type.
However, there is more reason to not importing a header file than just compile speed.
Not importing a header file also reduces your inadvertent access to extraneous classes.
For example, say you have a TrackFileChanges class that tracks the file system for changes to a specific file, and you have a CachedFile class that stores cached data from a file. The latter might use a private ivar of type TrackFileChanges*, but for uses of CachedFile, this is simply an implementation detail (ideally, the ivar would be auto-generated with a private property using the new runtime, but thats not possible if you're using the old run time).
So clients that #import "CachedFile.h" probably do not need or want access to TrackFileChanges.h. And if they do, they should make it clear by #importing it themselves. By using #class TrackFileChanges instea of #import "TrackFileChanges.h" in CachedFile.h you improve the encapsulation.
But all that said, there is nothing awrong with importing a header file from a second header file if the second header wants to expose the first to all clients. For example, header files that declare classes need to be imported directly in subclassing header files, and header files declaring protocols might well be imported directly (although youy can use #protocol ABC; to avoid this).
Go ahead and use #import. The only reason people recommend to use #class when possible is because it makes your code slightly faster to compile. However, there is no issue with #importing one .h file from another. In fact, you need to do this when extending another class.
If you are ok using compiler extensions, you could use this order in Clang:
enum Enum;
typedef enum Enum Enum2;
void f(Enum2); // ok. it sees this type's true name.
enum Enum {
E_1
};
// ok. now its declaration is visible and we can use it.
void f(Enum2 e) {
}
Note: It will trigger a -Wpedantic warning.
If you are using C++11, you should use their enums, which are safe to forward declare -- e.g. enum class Enum:uint8_t; (not a compiler extension).
What worked for a forward declaration of an enum for me in an Objective C .h file was look in the ProjectName-Swift.h file and see what it put, which happened to be the following:
enum SwiftEnumName : NSInteger;
I needed this forward declaration because I had a function parameter type of SwiftEnumName. And it wouldn't let me put the ProjectName-Swift.h import in the Objective C .h file.
Then in the Objective C .m file I just had the #import "ProjectName-Swift.h" in it and just used the SwiftEnum normally.
This was using Swift 4.1.2.
You'd have to either #import them anyway or create a separate header file containing only the typedef. Not importing header files in a header makes the compilation faster, but doesn't change anything else.
Why doesn't C++ support forward declaration of enums?