不可或缺 Windows Native (25) - C++: windows app native, android app native, ios app native

时间:2021-02-21 22:40:08

[源码下载]

不可或缺 Windows Native (25) - C++: windows app native, android app native, ios app native

作者:webabcd

介绍
不可或缺 Windows Native 之 C++

  • windows app native
  • android app native
  • ios app native

示例
一、演示 windows app native 开发
1、native 层
CppCx.h

#pragma once 

#include <string>

using namespace std;

namespace NativeDll
{
class CppCx
{
public:
string Hello(string name);
};
}

CppCx.cpp

/*
* 演示 C#, C++/CX, C/C++ 间的通信
*
* 本例是 C/C++ 部分
*/ #include "pch.h"
#include "CppCx.h"
#include "DemoCx.h" using namespace NativeDll; string CppCx::Hello(string name)
{
// C/C++ 通过 C++/CX 调用 C#
if (DemoCx::GlobalCallback != nullptr)
DemoCx::GlobalCallback->Cx2Cs("c/c++ to c++/cx to cs"); return "hello: " + name;
}

2、C++/CX 层
DemoCx.h

#pragma once 

#include "ICallback.h"

using namespace Platform;

namespace NativeDll
{
// ref class 可被输出到元数据(winmd - Windows Metadata),以便其他托管程序调用
public ref class DemoCx sealed
{
public:
// 用“^”标记的,系统会负责他们的引用计数,当引用计数为 0 时,它们会被销毁
String^ HelloCx(String^ name); String^ HelloCpp(String^ name); // 由 C# 调用,用于设置 ICallback 对象
void SetCallback(ICallback^ callback); // 由 C++/CX 调用,用于通过 ICallback 向 C# 发送数据
property static ICallback^ GlobalCallback;
};
}

DemoCx.cpp

/*
* 演示 C#, C++/CX, C/C++ 间的通信
*
* 本例是 C++/CX 部分
*
* 为了支持 Windows Runtime Component 这种方式,所以引入 Microsoft created the Visual C++ component extensions (C++/CX),可以将其看作是连接“调用者”和“C/C++”之间的桥梁,元数据是 windows metadata (.winmd) files
* 为了让“调用者”调用 Windows Runtime Component,所以 C++/CX 会有自己的一些数据类型,比如字符串是 Platform::String^ 类型的,这样才能让“调用者”调用
* 关于 C++/CX 的相关知识请参见:https://msdn.microsoft.com/en-us/library/hh755822.aspx
*/ #include "pch.h"
#include "DemoCx.h"
#include "CppCx.h"
#include "cppHelper.h" using namespace NativeDll; String^ DemoCx::HelloCx(String^ name)
{
// 如果 C# 端设置了 ICallback 对象,则可以在 C++/Cx 端向 C# 端发送数据
if (GlobalCallback != nullptr)
GlobalCallback->Cx2Cs("c++/cx to cs"); return "hello: " + name;
} // 由 C# 调用,用于设置 ICallback 对象
void DemoCx::SetCallback(ICallback^ callback)
{
GlobalCallback = callback;
} String^ DemoCx::HelloCpp(String^ name)
{
// C++/CX 与 C/C++ 通信时,如果要传递字符串,则要对字符串做转换
string cppName = ws2s_3(std::wstring(name->Data())); // C++/CX 调用 C/C++
CppCx cppCx;
string cppResult = cppCx.Hello(cppName);
String^ cxResult = ref new Platform::String(s2ws_3(cppResult).c_str()); return cxResult;
}

3、托管代码层
Cx.xaml

<Page
x:Class="NativeDemo.Demo.Cx"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:NativeDemo.Demo"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"> <Grid Background="Transparent"> <StackPanel Margin="120 0 0 0"> <TextBlock Name="lblMsg" TextAlignment="Left" FontSize="24.667" TextWrapping="Wrap" /> </StackPanel>
</Grid>
</Page>

Cx.xaml.cs

/*
* 演示 C#, C++/CX, C/C++ 间的通信
*
* 本例是 C# 部分
*
*
* C# 与 C++/CX 间通信;C++/CX 与 C/C++ 间通信;C# 通过 C++/CX 与 C/C++ 间通信
*/ using System;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Navigation; namespace NativeDemo.Demo
{
public sealed partial class Cx : Page
{
public Cx()
{
this.InitializeComponent();
} protected override void OnNavigatedTo(NavigationEventArgs e)
{
NativeDll.DemoCx demoCx = new NativeDll.DemoCx(); MyCallback myCallback = new MyCallback();
myCallback.MessageReceived += myCallback_MessageReceived;
demoCx.SetCallback(myCallback); // C# 调用 C++/CX
lblMsg.Text += demoCx.HelloCx("cs to c++/cx");
lblMsg.Text += Environment.NewLine; // C# 通过 C++/CX 调用 C/C++
lblMsg.Text += demoCx.HelloCpp("cs to c++/cx to c/c++");
lblMsg.Text += Environment.NewLine;
} async void myCallback_MessageReceived(object sender, MessageEventArgs e)
{
MyCallback myCallback = (MyCallback)sender; await lblMsg.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
{
lblMsg.Text += e.Message;
lblMsg.Text += Environment.NewLine;
});
}
} // 实现 C++/CX 中的 ICallback 接口
public class MyCallback : NativeDll.ICallback
{
// 收到 C++/CX 直接发送过来的数据,或者 C/C++ 通过 C++/CX 发送过来的数据
public void Cx2Cs(string message)
{
OnMessageReceived(new MessageEventArgs { Message = message });
} public event EventHandler<MessageEventArgs> MessageReceived;
protected virtual void OnMessageReceived(MessageEventArgs e)
{
EventHandler<MessageEventArgs> handler = MessageReceived;
if (handler != null)
handler(this, e);
}
} public class MessageEventArgs : EventArgs
{
public string Message { get; set; }
} }

二、演示 android app native 开发
1、native 层(C 语言)
cHello.h

#ifndef _MYHEAD_CHELLO_
#define _MYHEAD_CHELLO_
#ifdef __cplusplus
extern "C"
{
#endif char *hello(const char *name); #ifdef __cplusplus
}
#endif
#endif

cHello.c

#include "cHello.h"
#include <stdlib.h> char *str_concat2(const char *, const char *); char *hello(const char *name)
{
return str_concat2("hello: ", name);
} char *str_concat2(const char *str1, const char *str2)
{
char *result;
result = (char *)malloc(strlen(str1) + strlen(str2) + );
if (!result)
{
exit(EXIT_FAILURE);
} strncpy(result, str1, strlen(str1) + );
strncat(result, str2, strlen(str1) + strlen(str2) + );
return result;
}

2、native 层(C++)
CppHello.h

#ifndef _MYHEAD_CPPHELLO_
#define _MYHEAD_CPPHELLO_ #include <string> using namespace std; namespace MyNs
{
class CppHello
{
public:
string Hello(string name);
};
} #endif

CppHello.cpp

#include "CppHello.h"

using namespace MyNs;

string CppHello::Hello(string name)
{
return "hello: " + name;
}

3、jni 层
jniDemo.h

#include <jni.h>

#ifndef _Included_com_cnblogs_webabcd_jniDemo
#define _Included_com_cnblogs_webabcd_jniDemo
#ifdef __cplusplus
extern "C" {
#endif // 注意函数名的命名规则
JNIEXPORT jstring JNICALL Java_com_example_androidnative_MainActivity_helloJniCpp(JNIEnv *env, jobject obj, jstring name); JNIEXPORT jstring JNICALL Java_com_example_androidnative_MainActivity_helloJniC(JNIEnv *env, jobject obj, jstring name); #ifdef __cplusplus
}
#endif
#endif

jniDemo.cpp

/*
* jni(Java Native Interface) - 详细文档参见 http://docs.oracle.com/javase/7/docs/technotes/guides/jni/
* ndk(Native Development Kit) - 下载 ndk 后,其目录内有详细的文档
* cygwin - 在 windows 平台上运行的类 UNIX 模拟环境,可以调用 ndk 编译 so
*
*
* 为了使 jni 能支持 c++ 需要这么做:
* 1、将本文件的后缀名从 .c 修改为 .cpp(c++ 文件的扩展名可以通过 Android.mk 的 LOCAL_CPP_EXTENSION 指定)
* 2、按本例的方式配置 Application.mk 文件(如果只想支持 c 语言的话,则可以不要此文件)
*/ #include "jniDemo.h"
#include "CppHello.h"
#include "cHello.h" void jni2java(JNIEnv *); // 对应 MainActivity 类中的 public native String helloJniCpp(String name); 注意函数的命名规则
JNIEXPORT jstring JNICALL Java_com_example_androidnative_MainActivity_helloJniCpp(JNIEnv *env, jobject obj, jstring name)
{
jni2java(env); MyNs::CppHello cppHello;
const char *charName = env->GetStringUTFChars(name, ); // jstring to char
std::string stringName(charName); // jstring to string
std::string stringResult = cppHello.Hello(stringName);
const char *charResult = stringResult.data(); // string to char
jstring jstringResult = env->NewStringUTF(charResult); // char to jstring
return jstringResult; /*
* 调用 jni 函数时注意(对于 C 和 CPP 来说,JNIEnv 的含义不同,具体请查看文档):
* 1、C 的用法示例:jstring jstringResult = (*env)->NewStringUTF(env, charResult); // char to jstring
* 2、CPP 的用法示例:jstring jstringResult = env->NewStringUTF(charResult); // char to jstring
*/
} // 对应 MainActivity 类中的 public native String helloJniC(String name); 注意函数的命名规则
JNIEXPORT jstring JNICALL Java_com_example_androidnative_MainActivity_helloJniC(JNIEnv *env, jobject obj, jstring name)
{
jni2java(env); const char *charName = env->GetStringUTFChars(name, ); // jstring to char
char *charResult = hello(charName);
jstring jstringResult = env->NewStringUTF(charResult); // char to jstring
free(charResult);
return jstringResult;
} // 调用 MainActivity 类中的 public static void helloJava(String message); 函数
void jni2java(JNIEnv *env)
{
const char *className = "com/example/androidnative/MainActivity"; // 注意类名规则
jclass cla = env->FindClass(className);
// 第三个参数中:(Ljava/lang/String;)代表 java 中的被调用的函数的参数是 String 类型;V 代表 java 中的被调用的函数的返回值是 void 类型
jmethodID method = env->GetStaticMethodID(cla, "helloJava", "(Ljava/lang/String;)V");
jstring result = env->NewStringUTF("jni to java");
env->CallStaticVoidMethod(cla, method, result);
}

编译相关
Application.mk

APP_STL := stlport_static #以静态链接的方式使用stlport版本的STL
APP_CPPFLAGS := -fexceptions -frtti #允许异常功能,及运行时类型识别
APP_CPPFLAGS +=-std=c++ #允许使用c++11的函数等功能
APP_CPPFLAGS +=-fpermissive #此项有效时表示宽松的编译形式,比如没有用到的代码中有错误也可以通过编

Android.mk

LOCAL_PATH := $(call my-dir)

#模块1
include $(CLEAR_VARS) #清除 LOCAL_MODULE, LOCAL_SRC_FILES 之类的变量
LOCAL_CPP_EXTENSION := .cpp # C++ 文件的扩展名
LOCAL_MODULE := jniDemo # 模块名。如果模块名为“abc”,则此模块将会生成“libabc.so”文件。
LOCAL_SRC_FILES := jniDemo.cpp CppHello.cpp cHello.c # 需要编译的源文件
include $(BUILD_SHARED_LIBRARY) # 编译当前模块 #模块2

4、托管代码层
MainActivity.java

/*
* 演示 java 如何通过 jni 与 C/C++ 互相通信
*/ package com.example.androidnative; import android.app.Activity;
import android.os.Bundle;
import android.widget.TextView; public class MainActivity extends Activity { private static TextView txtMsg; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main); txtMsg = (TextView) this.findViewById(R.id.txtMsg); // 加载 so
System.loadLibrary("jniDemo"); // java 调用 jni, c
String resultC = helloJniC("java to jni to c");
txtMsg.append(resultC);
txtMsg.append("\n"); // java 调用 jni, c++
String resultCpp = helloJniCpp("java to jni to c++");
txtMsg.append(resultCpp);
txtMsg.append("\n");
} // native function(对应的 jni 函数参见 jniDemo.cpp)
public native String helloJniC(String name);
public native String helloJniCpp(String name); // jni 调用 java
public static void helloJava(String message)
{
txtMsg.append(message);
txtMsg.append("\n");
}
}

三、演示 ios app native 开发(无论是 oc 还是 swift 都是 native 开发,本例演示 oc, c, c++ 混编)
ViewController.h

//
// ViewController.h
// IosNative
//
// Created by wanglei on 4/24/15.
// Copyright (c) 2015 webabcd. All rights reserved.
// #import <UIKit/UIKit.h> @interface ViewController : UIViewController @end

ViewController.mm

/*
* 演示 objective-c 如何与 C/C++ 互相通信
*
* objective-c 是面向对象的 c 语言,本身就是 Native 的,完全兼容 c 语言,可以与 C/C++ 混编
*
* 注:为了支持 C++ 需要把本文件的后缀名由 .m 修改为 .mm
*/ #import "ViewController.h" #include <string> @interface ViewController () @end @implementation ViewController char *hello(const char *);
class CppHello; - (void)viewDidLoad
{
[super viewDidLoad]; [self helloC:@"oc to c"];
[self helloCpp:@"oc to c++"];
} // oc 调用 c
- (void)helloC:(NSString *)name
{
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(, , , )];
[self.view addSubview:label]; const char *charName = [name UTF8String]; // nsstring to char
char *charResult = hello(charName);
NSString *nsstringResult = [[NSString alloc] initWithCString:charResult encoding:NSUTF8StringEncoding]; // char to nsstring
free(charResult); label.text = nsstringResult;
} // oc 调用 c++
- (void)helloCpp:(NSString *)name
{
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(, , , )];
[self.view addSubview:label]; string stringName = [name UTF8String]; // nsstring to string
MyNs::CppHello cppHello;
string stringResult = cppHello.Hello(stringName);
NSString *nsstringResult = [[NSString alloc] initWithCString:stringResult.c_str() encoding:NSUTF8StringEncoding]; // string to nsstring label.text = nsstringResult;
} char *hello(const char *name)
{
// c 调用 oc(别忘了 #import <Foundation/Foundation.h>,本例中不用是因为 UIViewController 已经导入这个头文件了)
NSLog(@"c to oc"); char *s = "hello: "; char *result;
result = (char *)malloc(strlen(s) + strlen(name) + );
if (!result)
exit(EXIT_FAILURE); strncpy(result, s, strlen(s) + );
strncat(result, name, strlen(s) + strlen(name) + ); return result;
} using namespace std;
namespace MyNs
{
class CppHello
{
public:
string Hello(string name);
};
}
string MyNs::CppHello::Hello(string name)
{
// c++ 调用 oc(别忘了 #import <Foundation/Foundation.h>,本例中不用是因为 UIViewController 已经导入这个头文件了)
NSLog(@"c++ to oc"); return "hello: " + name;
} @end

OK
[源码下载]