mirror of
https://github.com/Sunnyyoung/WeChatTweak-macOS.git
synced 2025-07-07 07:56:06 +08:00
Add dock menu item
This commit is contained in:
parent
e1cb4bf7d2
commit
5dd8ca7a40
13
JRSwizzle.h
Normal file
13
JRSwizzle.h
Normal file
|
@ -0,0 +1,13 @@
|
|||
// JRSwizzle.h semver:1.0
|
||||
// Copyright (c) 2007-2011 Jonathan 'Wolf' Rentzsch: http://rentzsch.com
|
||||
// Some rights reserved: http://opensource.org/licenses/MIT
|
||||
// https://github.com/rentzsch/jrswizzle
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface NSObject (JRSwizzle)
|
||||
|
||||
+ (BOOL)jr_swizzleMethod:(SEL)origSel_ withMethod:(SEL)altSel_ error:(NSError**)error_;
|
||||
+ (BOOL)jr_swizzleClassMethod:(SEL)origSel_ withClassMethod:(SEL)altSel_ error:(NSError**)error_;
|
||||
|
||||
@end
|
134
JRSwizzle.m
Normal file
134
JRSwizzle.m
Normal file
|
@ -0,0 +1,134 @@
|
|||
// JRSwizzle.m semver:1.0
|
||||
// Copyright (c) 2007-2011 Jonathan 'Wolf' Rentzsch: http://rentzsch.com
|
||||
// Some rights reserved: http://opensource.org/licenses/MIT
|
||||
// https://github.com/rentzsch/jrswizzle
|
||||
|
||||
#import "JRSwizzle.h"
|
||||
|
||||
#if TARGET_OS_IPHONE
|
||||
#import <objc/runtime.h>
|
||||
#import <objc/message.h>
|
||||
#else
|
||||
#import <objc/objc-class.h>
|
||||
#endif
|
||||
|
||||
#define SetNSErrorFor(FUNC, ERROR_VAR, FORMAT,...) \
|
||||
if (ERROR_VAR) { \
|
||||
NSString *errStr = [NSString stringWithFormat:@"%s: " FORMAT,FUNC,##__VA_ARGS__]; \
|
||||
*ERROR_VAR = [NSError errorWithDomain:@"NSCocoaErrorDomain" \
|
||||
code:-1 \
|
||||
userInfo:[NSDictionary dictionaryWithObject:errStr forKey:NSLocalizedDescriptionKey]]; \
|
||||
}
|
||||
#define SetNSError(ERROR_VAR, FORMAT,...) SetNSErrorFor(__func__, ERROR_VAR, FORMAT, ##__VA_ARGS__)
|
||||
|
||||
#if OBJC_API_VERSION >= 2
|
||||
#define GetClass(obj) object_getClass(obj)
|
||||
#else
|
||||
#define GetClass(obj) (obj ? obj->isa : Nil)
|
||||
#endif
|
||||
|
||||
@implementation NSObject (JRSwizzle)
|
||||
|
||||
+ (BOOL)jr_swizzleMethod:(SEL)origSel_ withMethod:(SEL)altSel_ error:(NSError**)error_ {
|
||||
#if OBJC_API_VERSION >= 2
|
||||
Method origMethod = class_getInstanceMethod(self, origSel_);
|
||||
if (!origMethod) {
|
||||
#if TARGET_OS_IPHONE
|
||||
SetNSError(error_, @"original method %@ not found for class %@", NSStringFromSelector(origSel_), [self class]);
|
||||
#else
|
||||
SetNSError(error_, @"original method %@ not found for class %@", NSStringFromSelector(origSel_), [self className]);
|
||||
#endif
|
||||
return NO;
|
||||
}
|
||||
|
||||
Method altMethod = class_getInstanceMethod(self, altSel_);
|
||||
if (!altMethod) {
|
||||
#if TARGET_OS_IPHONE
|
||||
SetNSError(error_, @"alternate method %@ not found for class %@", NSStringFromSelector(altSel_), [self class]);
|
||||
#else
|
||||
SetNSError(error_, @"alternate method %@ not found for class %@", NSStringFromSelector(altSel_), [self className]);
|
||||
#endif
|
||||
return NO;
|
||||
}
|
||||
|
||||
class_addMethod(self,
|
||||
origSel_,
|
||||
class_getMethodImplementation(self, origSel_),
|
||||
method_getTypeEncoding(origMethod));
|
||||
class_addMethod(self,
|
||||
altSel_,
|
||||
class_getMethodImplementation(self, altSel_),
|
||||
method_getTypeEncoding(altMethod));
|
||||
|
||||
method_exchangeImplementations(class_getInstanceMethod(self, origSel_), class_getInstanceMethod(self, altSel_));
|
||||
return YES;
|
||||
#else
|
||||
// Scan for non-inherited methods.
|
||||
Method directOriginalMethod = NULL, directAlternateMethod = NULL;
|
||||
|
||||
void *iterator = NULL;
|
||||
struct objc_method_list *mlist = class_nextMethodList(self, &iterator);
|
||||
while (mlist) {
|
||||
int method_index = 0;
|
||||
for (; method_index < mlist->method_count; method_index++) {
|
||||
if (mlist->method_list[method_index].method_name == origSel_) {
|
||||
assert(!directOriginalMethod);
|
||||
directOriginalMethod = &mlist->method_list[method_index];
|
||||
}
|
||||
if (mlist->method_list[method_index].method_name == altSel_) {
|
||||
assert(!directAlternateMethod);
|
||||
directAlternateMethod = &mlist->method_list[method_index];
|
||||
}
|
||||
}
|
||||
mlist = class_nextMethodList(self, &iterator);
|
||||
}
|
||||
|
||||
// If either method is inherited, copy it up to the target class to make it non-inherited.
|
||||
if (!directOriginalMethod || !directAlternateMethod) {
|
||||
Method inheritedOriginalMethod = NULL, inheritedAlternateMethod = NULL;
|
||||
if (!directOriginalMethod) {
|
||||
inheritedOriginalMethod = class_getInstanceMethod(self, origSel_);
|
||||
if (!inheritedOriginalMethod) {
|
||||
SetNSError(error_, @"original method %@ not found for class %@", NSStringFromSelector(origSel_), [self className]);
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
if (!directAlternateMethod) {
|
||||
inheritedAlternateMethod = class_getInstanceMethod(self, altSel_);
|
||||
if (!inheritedAlternateMethod) {
|
||||
SetNSError(error_, @"alternate method %@ not found for class %@", NSStringFromSelector(altSel_), [self className]);
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
|
||||
int hoisted_method_count = !directOriginalMethod && !directAlternateMethod ? 2 : 1;
|
||||
struct objc_method_list *hoisted_method_list = malloc(sizeof(struct objc_method_list) + (sizeof(struct objc_method)*(hoisted_method_count-1)));
|
||||
hoisted_method_list->obsolete = NULL; // soothe valgrind - apparently ObjC runtime accesses this value and it shows as uninitialized in valgrind
|
||||
hoisted_method_list->method_count = hoisted_method_count;
|
||||
Method hoisted_method = hoisted_method_list->method_list;
|
||||
|
||||
if (!directOriginalMethod) {
|
||||
bcopy(inheritedOriginalMethod, hoisted_method, sizeof(struct objc_method));
|
||||
directOriginalMethod = hoisted_method++;
|
||||
}
|
||||
if (!directAlternateMethod) {
|
||||
bcopy(inheritedAlternateMethod, hoisted_method, sizeof(struct objc_method));
|
||||
directAlternateMethod = hoisted_method;
|
||||
}
|
||||
class_addMethods(self, hoisted_method_list);
|
||||
}
|
||||
|
||||
// Swizzle.
|
||||
IMP temp = directOriginalMethod->method_imp;
|
||||
directOriginalMethod->method_imp = directAlternateMethod->method_imp;
|
||||
directAlternateMethod->method_imp = temp;
|
||||
|
||||
return YES;
|
||||
#endif
|
||||
}
|
||||
|
||||
+ (BOOL)jr_swizzleClassMethod:(SEL)origSel_ withClassMethod:(SEL)altSel_ error:(NSError**)error_ {
|
||||
return [GetClass((id)self) jr_swizzleMethod:origSel_ withMethod:altSel_ error:error_];
|
||||
}
|
||||
|
||||
@end
|
|
@ -8,12 +8,14 @@
|
|||
|
||||
## 截图
|
||||
|
||||

|
||||

|
||||

|
||||
|
||||
## 功能
|
||||
|
||||
- 阻止消息撤回
|
||||
- 客户端无限多开
|
||||
- 右键 Dock Icon 登录新的微信账号
|
||||
- 命令行执行:`open -n /Applications/WeChat.app`
|
||||
|
||||
## 使用
|
||||
|
@ -33,6 +35,7 @@
|
|||
|
||||
## 依赖
|
||||
|
||||
- [JRSwizzle](https://github.com/rentzsch/jrswizzle)
|
||||
- [insert_dylib](https://github.com/Tyilo/insert_dylib)
|
||||
|
||||
## License
|
||||
|
|
|
@ -8,17 +8,19 @@ A dynamic library tweak for WeChat macOS.
|
|||
|
||||
## Screenshot
|
||||
|
||||

|
||||

|
||||

|
||||
|
||||
## Feature
|
||||
|
||||
- Prevent message revoked
|
||||
- Multiple WeChat Instance
|
||||
- Right click on the Dock icon to login another WeChat account
|
||||
- Run command: `open -n /Applications/WeChat.app`
|
||||
|
||||
## Usage
|
||||
|
||||
- `sudo make install` Inject the dylib to `WeChat` by [insert_dylib](https://github.com/Tyilo/insert_dylib)
|
||||
- `sudo make install` Inject the dylib to `WeChat`
|
||||
- `sudo make uninstall` Uninstall the injection
|
||||
|
||||
## Development
|
||||
|
@ -33,6 +35,7 @@ Run `xcode-select --install` to install Command Line Tools.
|
|||
|
||||
## Dependency
|
||||
|
||||
- [JRSwizzle](https://github.com/rentzsch/jrswizzle)
|
||||
- [insert_dylib](https://github.com/Tyilo/insert_dylib)
|
||||
|
||||
## License
|
||||
|
|
Before Width: | Height: | Size: 48 KiB After Width: | Height: | Size: 48 KiB |
BIN
Screenshot/0x02.png
Normal file
BIN
Screenshot/0x02.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 174 KiB |
Binary file not shown.
|
@ -2,34 +2,51 @@
|
|||
#import <objc/runtime.h>
|
||||
#import <objc/message.h>
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#import "JRSwizzle.h"
|
||||
|
||||
// Tweak for no revoke message.
|
||||
__attribute__((constructor(101))) static void noRevokeTweak(void) {
|
||||
Class class = NSClassFromString(@"MessageService");
|
||||
SEL selector = NSSelectorFromString(@"onRevokeMsg:");
|
||||
Method method = class_getInstanceMethod(class, selector);
|
||||
IMP imp = imp_implementationWithBlock(^(id self, NSString *message) {
|
||||
NSRange begin = [message rangeOfString:@"<replacemsg><![CDATA["];
|
||||
NSRange end = [message rangeOfString:@"]]></replacemsg>"];
|
||||
NSRange subRange = NSMakeRange(begin.location + begin.length,end.location - begin.location - begin.length);
|
||||
@implementation NSObject (WeChatTweak)
|
||||
|
||||
NSString *informativeText = [message substringWithRange:subRange];
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
NSUserNotification *userNotification = [[NSUserNotification alloc] init];
|
||||
userNotification.informativeText = informativeText;
|
||||
[[NSUserNotificationCenter defaultUserNotificationCenter] deliverNotification:userNotification];
|
||||
});
|
||||
});
|
||||
class_replaceMethod(class, selector, imp, method_getTypeEncoding(method));
|
||||
#pragma mark - Constructor
|
||||
|
||||
static void __attribute__((constructor)) tweak(void) {
|
||||
[objc_getClass("MessageService") jr_swizzleMethod:NSSelectorFromString(@"onRevokeMsg:") withMethod:@selector(tweak_OnRevokeMsg:) error:nil];
|
||||
[objc_getClass("CUtility") jr_swizzleClassMethod:NSSelectorFromString(@"HasWechatInstance") withClassMethod:@selector(tweak_HasWechatInstance) error:nil];
|
||||
class_addMethod(objc_getClass("AppDelegate"), @selector(applicationDockMenu:), method_getImplementation(class_getInstanceMethod(objc_getClass("AppDelegate"), @selector(applicationDockMenu:))), "@:@");
|
||||
}
|
||||
|
||||
// Tweak for multiple instance.
|
||||
__attribute__((constructor(102))) static void multipleInstanceTweak(void) {
|
||||
Class class = object_getClass(NSClassFromString(@"CUtility"));
|
||||
SEL selector = NSSelectorFromString(@"HasWechatInstance");
|
||||
Method method = class_getInstanceMethod(class, selector);
|
||||
IMP imp = imp_implementationWithBlock(^(id self) {
|
||||
return 0;
|
||||
#pragma mark - No Revoke Message
|
||||
|
||||
- (void)tweak_OnRevokeMsg:(NSString *)message {
|
||||
NSRange begin = [message rangeOfString:@"<replacemsg><![CDATA["];
|
||||
NSRange end = [message rangeOfString:@"]]></replacemsg>"];
|
||||
NSRange subRange = NSMakeRange(begin.location + begin.length,end.location - begin.location - begin.length);
|
||||
NSString *informativeText = [message substringWithRange:subRange];
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
NSUserNotification *userNotification = [[NSUserNotification alloc] init];
|
||||
userNotification.informativeText = informativeText;
|
||||
[[NSUserNotificationCenter defaultUserNotificationCenter] deliverNotification:userNotification];
|
||||
});
|
||||
class_replaceMethod(class, selector, imp, method_getTypeEncoding(method));
|
||||
}
|
||||
|
||||
#pragma mark - Mutiple Instance
|
||||
|
||||
+ (BOOL)tweak_HasWechatInstance {
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (NSMenu *)applicationDockMenu:(NSApplication *)sender {
|
||||
NSMenu *menu = [[objc_getClass("NSMenu") alloc] init];
|
||||
NSMenuItem *menuItem = [[objc_getClass("NSMenuItem") alloc] initWithTitle:@"登录新的微信账号" action:@selector(openNewWeChatInstace:) keyEquivalent:@""];
|
||||
[menu insertItem:menuItem atIndex:0];
|
||||
return menu;
|
||||
}
|
||||
|
||||
- (void)openNewWeChatInstace:(id)sender {
|
||||
NSString *applicationPath = [[objc_getClass("NSBundle") mainBundle] bundlePath];
|
||||
NSTask *task = [[objc_getClass("NSTask") alloc] init];
|
||||
task.launchPath = @"/usr/bin/open";
|
||||
task.arguments = @[@"-n", applicationPath];
|
||||
[task launch];
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
Loading…
Reference in New Issue
Block a user