Manage Unhandled Exception and Crashes in a Mac Cocoa App - objective-c

I am implementing a Mac App and I want to handle following events:
Unhandled Exception
Program Crash (memory error dcc)
If I detect them, I can send details to me to analyze and fix bugs using one of the Crash Handlers that I found. Alas I am unable to figure out how to intercept crashes and exceptions.
First question: Have I to differentiate Exceptions from Crashes ? Or detecting Exception is enough?
How can I catch Exceptions and/or crashes redirecting them to my handler ?
PS
I tried following in my MyApp class
NSSetUncaughtExceptionHandler(&uncaughtExceptionHandler);
signal(SIGABRT, SignalHandler);
signal(SIGILL, SignalHandler);
signal(SIGSEGV, SignalHandler);
signal(SIGFPE, SignalHandler);
signal(SIGBUS, SignalHandler);
signal(SIGPIPE, SignalHandler);
but it doesn't work. Every time it crashes, it goes to debugger without classing SignalHandler or uncaughtExceptionHandler

I have found the best way is to create a simple Exception handling delegate class as this allows exceptions in IBAction methods to be caught.
main.mm:
#interface ExceptionDelegate : NSObject
#end
static ExceptionDelegate *exceptionDelegate = nil;
int main(int argc, char **argv)
{
int retval = 1;
#autoreleasepool
{
//
// Set exception handler delegate
//
exceptionDelegate = [[ExceptionDelegate alloc] init];
NSExceptionHandler *exceptionHandler = [NSExceptionHandler defaultExceptionHandler];
exceptionHandler.exceptionHandlingMask = NSLogAndHandleEveryExceptionMask;
exceptionHandler.delegate = exceptionDelegate;
//
// Set signal handler
//
int signals[] =
{
SIGQUIT, SIGILL, SIGTRAP, SIGABRT, SIGEMT, SIGFPE, SIGBUS, SIGSEGV,
SIGSYS, SIGPIPE, SIGALRM, SIGXCPU, SIGXFSZ
};
const unsigned numSignals = sizeof(signals) / sizeof(signals[0]);
struct sigaction sa;
sa.sa_sigaction = signalHandler;
sa.sa_flags = SA_SIGINFO;
sigemptyset(&sa.sa_mask);
for (unsigned i = 0; i < numSignals; i++)
sigaction(signals[i], &sa, NULL);
....
}
....
return retval;
}
static void signalHandler(int sig, siginfo_t *info, void *context)
{
logerr(#"Caught signal %d", sig);
exit(102);
}
#implementation ExceptionDelegate
- (BOOL)exceptionHandler:(NSExceptionHandler *)exceptionHandler
shouldLogException:(NSException *)exception
mask:(unsigned int)mask
{
logerr(#"An unhandled exception occurred: %#", [exception reason]);
return YES;
}
- (BOOL)exceptionHandler:(NSExceptionHandler *)exceptionHandler
shouldHandleException:(NSException *)exception
mask:(unsigned int)mask
{
exit(101);
// not reached
return NO;
}
#end
You'll need to add the ExceptionHandling.framework to your project.

Related

Setting media playback info in MacOs not working

I'm a newbie in ObjC and MacOs development.
My final goal is understand how setting playback info works from ObjC to try to implement that later on Rust (using generated ObjC runtime bindings).
So right now I'm trying to write small piece of code on ObjC that would set playback info (without actually playing anything).
I have found https://github.com/MarshallOfSound/electron-media-service/blob/master/src/darwin/service.mm and used it as a base.
Here's code I have right now:
#import <Foundation/Foundation.h>
#import <AppKit/AppKit.h>
#import <objc/runtime.h>
#include "MediaPlayer/MPNowPlayingInfoCenter.h"
#include "MediaPlayer/MPRemoteCommandCenter.h"
#include "MediaPlayer/MPRemoteCommand.h"
#include "MediaPlayer/MPMediaItem.h"
#include "MediaPlayer/MPRemoteCommandEvent.h"
#interface NativeMediaController : NSObject { }
#end
#implementation NativeMediaController
- (MPRemoteCommandHandlerStatus)remotePlay {
NSLog(#"Play");
return MPRemoteCommandHandlerStatusSuccess;
}
- (MPRemoteCommandHandlerStatus)remotePause {
NSLog(#"Pause");
return MPRemoteCommandHandlerStatusSuccess;
}
- (MPRemoteCommandHandlerStatus)remoteTogglePlayPause {
NSLog(#"PlayPause");
return MPRemoteCommandHandlerStatusSuccess;
}
- (MPRemoteCommandHandlerStatus)remoteNext {
NSLog(#"Next");
return MPRemoteCommandHandlerStatusSuccess;
}
- (MPRemoteCommandHandlerStatus)remotePrev {
NSLog(#"Previous");
return MPRemoteCommandHandlerStatusSuccess;
}
- (MPRemoteCommandHandlerStatus)remoteChangePlaybackPosition:(MPChangePlaybackPositionCommandEvent*)event {
NSLog(#"ChangePlaybackPosition");
return MPRemoteCommandHandlerStatusSuccess;
}
- (MPRemoteCommandHandlerStatus)move:(MPChangePlaybackPositionCommandEvent*)event {
NSLog(#"Move");
return MPRemoteCommandHandlerStatusSuccess;
}
#end
int main(int argc, const char * argv[]) {
#autoreleasepool {
// insert code here...
NSLog(#"Hello, World!");
NativeMediaController* controller = [[NativeMediaController alloc] init];
MPRemoteCommandCenter *remoteCommandCenter = [MPRemoteCommandCenter sharedCommandCenter];
[remoteCommandCenter playCommand].enabled = true;
[remoteCommandCenter pauseCommand].enabled = true;
[remoteCommandCenter togglePlayPauseCommand].enabled = true;
[remoteCommandCenter changePlaybackPositionCommand].enabled = true;
[remoteCommandCenter nextTrackCommand].enabled = true;
[remoteCommandCenter previousTrackCommand].enabled = true;
[[remoteCommandCenter playCommand] addTarget:controller action:#selector(remotePlay)];
[[remoteCommandCenter pauseCommand] addTarget:controller action:#selector(remotePause)];
[[remoteCommandCenter togglePlayPauseCommand] addTarget:controller action:#selector(remoteTogglePlayPause)];
[[remoteCommandCenter changePlaybackPositionCommand] addTarget:controller action:#selector(remoteChangePlaybackPosition:)];
[[remoteCommandCenter nextTrackCommand] addTarget:controller action:#selector(remoteNext)];
[[remoteCommandCenter previousTrackCommand] addTarget:controller action:#selector(remotePrev)];
NSMutableDictionary *songInfo = [[NSMutableDictionary alloc] init];
[songInfo setObject:[NSString stringWithUTF8String:"Test title"] forKey:MPMediaItemPropertyTitle];
[songInfo setObject:[NSString stringWithUTF8String:"Test artist"] forKey:MPMediaItemPropertyArtist];
[songInfo setObject:[NSString stringWithUTF8String:"Test albumtitle"] forKey:MPMediaItemPropertyAlbumTitle];
[songInfo setObject:[NSNumber numberWithFloat:60.0] forKey:MPNowPlayingInfoPropertyElapsedPlaybackTime];
[songInfo setObject:[NSNumber numberWithFloat:360.0] forKey:MPMediaItemPropertyPlaybackDuration];
[songInfo setObject:[NSNumber numberWithFloat:112233] forKey:MPMediaItemPropertyPersistentID];
[MPNowPlayingInfoCenter defaultCenter].playbackState = MPNowPlayingPlaybackStatePlaying;
[[MPNowPlayingInfoCenter defaultCenter] setNowPlayingInfo:songInfo];
NSLog(#"End!");
char input[50] = {0};
printf("Enter anything to quit: ");
scanf("%s", input);
}
return 0;
}
When this code is executed I see no effect in MacOs playing info widget.
Unfortunately right now I have no idea how to debug this or where can I find better ObjC example.
Finally, I found the reason that caused my problem playing with Apple Swift example.
There're two requirements to show playback widget (i didn't find them documented anywhere) that have been missing:
You should register a callback for at least one action (play, pause)
You should build your application as an App, not as Console. If your program is not shown in the dock as a running app - the playback widget wouldn't work.

How to use dispatch_queue_set_specific() and dispatch_get_specific()

I'm having a hard time finding good examples on how to use these functions.
static void * kQueue1Key = "key1";
static void * kQueue2Key = "key2";
dispatch_queue_t queue1 = dispatch_queue_create("com.company.queue1", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t queue2 = dispatch_queue_create("com.company.queue2", DISPATCH_QUEUE_SERIAL);
dispatch_queue_set_specific(queue1, kQueue1Key, (void *)kQueue1Key, NULL);
dispatch_queue_set_specific(queue2, kQueue2Key, (void *)kQueue2Key, NULL);
dispatch_sync(queue1, ^{
if(dispatch_get_specific(kQueue1Key))
{
NSLog(#"I'm expecting this line to run (A)");
dispatch_sync(queue2, ^{
NSLog(#"I'm expecting this line to run (B)");
if(dispatch_get_specific(kQueue2Key))
{
if(dispatch_get_specific(kQueue1Key))
{
NSLog(#"I'm expecting this line to run (C)");
}
else
{
[NSException raise:NSInternalInconsistencyException format:#"Should not end up here (C)"];
}
}
else
{
[NSException raise:NSInternalInconsistencyException format:#"Should not end up here (B)"];
}
});
}
else
{
[NSException raise:NSInternalInconsistencyException format:#"Should not end up here (A)"];
}
});
Result
I'm expecting this line to run (A)
I'm expecting this line to run (B)
*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Should not end up here (C)'
Is it expected behavior? If I were to dispatch_sync to queue1 since I'm not on the queue I would deadlock. What am I missing?
Oh here, it popped into my head why you're getting what you're getting. Notes in line:
dispatch_sync(queue1, ^{
When you get to this point, the "current queue" is queue1
if(dispatch_get_specific(kQueue1Key))
You're asking the current queue for the value it has for kQueue1Key, you set that earlier, so it gives it back to you.
{
NSLog(#"I'm expecting this line to run (A)");
dispatch_sync(queue2, ^{
When you get to this point, the "current queue" is now queue2
NSLog(#"I'm expecting this line to run (B)");
if(dispatch_get_specific(kQueue2Key))
You're asking the current queue for the value it has for kQueue2Key, you set that earlier, so it gives it back to you.
{
if(dispatch_get_specific(kQueue1Key))
You're now asking the current queue for the value it has for kQueue1Key. Since the current queue is queue2 and you never set a value with kQueue1Key on queue2 you get back NULL.
{
NSLog(#"I'm expecting this line to run (C)");
}
else
{
[NSException raise:NSInternalInconsistencyException format:#"Should not end up here (C)"];
}
The misunderstanding here is that dispatch_get_specific doesn't traverse the stack of nested queues, it traverses the queue targeting lineage. For instance, if you did this instead,
static void * kQueue1Key = (void*)"key1";
static void * kQueue2Key = (void*)"key2";
dispatch_queue_t queue1 = dispatch_queue_create("com.company.queue1", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t queue2 = dispatch_queue_create("com.company.queue2", DISPATCH_QUEUE_SERIAL);
dispatch_queue_set_specific(queue1, kQueue1Key, (void *)kQueue1Key, NULL);
dispatch_queue_set_specific(queue2, kQueue2Key, (void *)kQueue2Key, NULL);
// Set Queue2 to target Queue1
dispatch_set_target_queue(queue2, queue1);
dispatch_sync(queue2, ^{
if(dispatch_get_specific(kQueue1Key))
{
NSLog(#"I'm expecting this line to run (A)");
}
else
{
[NSException raise:NSInternalInconsistencyException format:#"Should not end up here (C)"];
}
if(dispatch_get_specific(kQueue2Key))
{
NSLog(#"I'm expecting this line to run (B)");
}
else
{
[NSException raise:NSInternalInconsistencyException format:#"Should not end up here (C)"];
}
});
...the targeting relationship is the one that gets traversed, not the stack relationship. It would be nice if there were something that traversed the stack relationship, but I'm not aware of anything (that you wouldn't have to implement yourself).
As mentioned in my comment, recursive locking using dispatch_sync is, in the general case, not possible due to the possibility of non-default queue targeting. For what it's worth, given/assuming default queue targeting, here is one possible approach:
#import <unordered_set>
#import <pthread.h>
static dispatch_once_t recursiveLockWithDispatchQueueTLSKeyOnceToken;
static pthread_key_t recursiveLockWithDispatchQueueTLSKey;
typedef std::unordered_multiset<const void*> RecursiveLockQueueBag;
static void freeRecursiveLockWithDispatchQueueTLSValue(void* tlsValue)
{
RecursiveLockQueueBag* ms = reinterpret_cast<RecursiveLockQueueBag*>(tlsValue);
if (ms) delete ms;
}
static inline BOOL queueStackCheck(dispatch_queue_t q, BOOL checkAndPushNotPop) // If yes, check and push if not on. If no, pop.
{
dispatch_once(&recursiveLockWithDispatchQueueTLSKeyOnceToken, ^{
pthread_key_create(&recursiveLockWithDispatchQueueTLSKey, freeRecursiveLockWithDispatchQueueTLSValue);
});
RecursiveLockQueueBag* ms = reinterpret_cast<RecursiveLockQueueBag*>(pthread_getspecific(recursiveLockWithDispatchQueueTLSKey));
if (!ms)
{
ms = new RecursiveLockQueueBag();
pthread_setspecific(recursiveLockWithDispatchQueueTLSKey, reinterpret_cast<const void*>(ms));
}
const void* const vpq = reinterpret_cast<const void*>((__bridge const void*)q);
BOOL alreadyOn = NO;
if (checkAndPushNotPop)
{
alreadyOn = (ms->count(vpq) > 0);
if (!alreadyOn)
{
ms->insert(vpq);
}
}
else
{
ms->erase(vpq);
}
return alreadyOn;
}
void dispatch_recursive_sync(dispatch_queue_t queue, dispatch_block_t block)
{
if (queueStackCheck(queue, YES))
{
block();
}
else
{
#try
{
dispatch_sync(queue, block);
}
#finally
{
queueStackCheck(queue, NO);
}
}
}
#implementation MyAppDelegate
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
dispatch_queue_t a = dispatch_queue_create("a", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t b = dispatch_queue_create("b", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t c = dispatch_queue_create("c", DISPATCH_QUEUE_SERIAL);
//dispatch_set_target_queue(a, c);
dispatch_recursive_sync(a, ^{
dispatch_recursive_sync(b, ^{
dispatch_recursive_sync(c, ^{
dispatch_recursive_sync(a, ^{
dispatch_recursive_sync(b, ^{
dispatch_recursive_sync(c, ^{
dispatch_recursive_sync(a, ^{
NSLog(#"got there");
});
});
});
});
});
});
});
}
#end
This is the lowest-overhead implementation I could think of in a few minutes. I used C++ to avoid message sending overhead. It requires that all uses of the queue use this function. This can be useful when there's a private queue protecting internal state of an object (i.e. where the queue is private and therefore guaranteed not to be retargeted, and where you can easily ensure that all consumers of the queue use dispatch_recursive_sync.

EXC_BAD_INSTRUCTION when using NSRunAlertPanel

To show errors on some condition, I am using NSRunAlertPanel on Mac OS X (on the code which is ported from Windows, where I was using MessageBox).
Before actual creation of Windows, some code is being run and calling this code to show some conditional error.
In a thread com.apple.libdispatch-manager, under following call stack
0 _dispatch_mgr_invoke
1 _dispatch_mgr_thread
it is giving EXC_BAD_INSTRUCTION
Is it because Windows is not created before NSRunAlertPanel?
What is the reason of this runtime error? What is exact alternative of MessageBox on Mac OS X?
Long ShowDebugMessageBox (const wchar_t * Message, const wchar_t * Title)
{
NSString * message; ///< Message.
NSString * title; ///< Title.
NSInteger response; ///< response.
message = WideToNSString (Message);
title = WideToNSString (Title);
//response = NSRunAlertPanel(title, message, #"Yes", #"No", #"Cancel");
response = NSRunCriticalAlertPanel (title, message, #"Okay", #"Cancel", nil);
switch(response) {
case NSAlertDefaultReturn:
return IDYES;
case NSAlertAlternateReturn:
return IDNO;
default:
return IDCANCEL;
}
}
NSString * WideToNSString (const wchar_t * Str)
{
if(!Str) {
return nil;
}
NSString * str; ///< String in NSString.
#if CP_SIZEOFWCHAR == 4
str = [[NSString alloc] initWithBytes: (CVPtr) Str
length: sizeof(wchar_t)* wcslen(Str)
encoding: NSUTF32LittleEndianStringEncoding];
//encoding: NSUTF32StringEncoding];
#else
str = [[NSString alloc] initWithBytes: (CVPtr) Str
length: sizeof(wchar_t)* wcslen(Str);
encoding: NSUTF16LittleEndianStringEncoding];
//encoding: NSUTF16StringEncoding];
#endif
return str;
}
class File {
public:
int Open(char * fname, int mode)
{
fd = open(fname, mode);
}
int Close()
{
close(fd);
//fd = 0; //CAUSE of the PROBLEM
}
~File ()
{
//ALERT Display message box about the error.
ALERT(fd != 0);
}
private:
int fd;
};
This is the code to show the message box.
Code to get NSString from wchar_t * string (Wide string) is perfectly fine and was tested. It is used in many places and running fine.
Same code on the other application (which creates Window first) is running fine.
Problem occurs when the destructor of File is called. Since fd is not 0, it shows message box and cause the problem.
When fd is set to 0, no alert box is displayed for constructor. However, other alert are shown but no problem occurred.
Is it due fd?
You haven't provided enough information to say what is causing the exception (please show the code).
I use NSRunCriticalAlertPanel() to display fatal errors in my app, which I am able to call pretty much any time I like:
void criticalAlertPanel(NSString *title, NSString *fmt, ...)
{
va_list va;
va_start(va, fmt);
NSString *message = [[NSString alloc] initWithFormat:fmt arguments:va];
va_end(va);
NSRunCriticalAlertPanel(title, message, #"OK", nil, nil);
}
(this code is ARC enabled).

Threading and Sockets in Objective-C

NOTE: I've edited my question. I've got it to connect and perform the first callback, but subsequent callbacks don't go through at all.
This is my first time writing Objective-C (with GNUstep; it's for a homework assignment). I've got the solution working, but I am trying to add something more to it. The app is a GUI client that connects to a server and gets data from it. Multiple clients can connect to the same server. If any one of the clients changes data that is residing on the server, the server sends a callback to all registered clients. This solution was originally implemented in Java (both client and server) and for the latest assignment, the professor wanted us to write an Objective-C client for it. He said that we don't need to handle callbacks, but I wanted to try anyway.
I am using NSThread and I wrote something that looks like this:
CallbackInterceptorThread.h
#import <Foundation/Foundation.h>
#import "AppDelegate.h"
#interface CallbackInterceptorThread : NSThread {
#private
NSString* clientPort;
AppDelegate* appDelegate;
}
- (id) initWithClientPort: (NSString*) aClientPort
appDelegate: (AppDelegate*) anAppDelegate;
- (void) main;
#end
CallbackInterceptorThread.m
#import <Foundation/Foundation.h>
#import "CallbackInterceptorThread.h"
#define MAXDATASIZE 4096
#implementation CallbackInterceptorThread
- (id) initWithClientPort: (NSString*) aClientPort
appDelegate: (AppDelegate*) anAppDelegate {
if((self = [super init])) {
[clientPort autorelease];
clientPort = [aClientPort retain];
[appDelegate autorelease];
appDelegate = [anAppDelegate retain];
}
return self;
}
- (void) main {
GSRegisterCurrentThread();
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
char* buffer = malloc(MAXDATASIZE);
Cst420ServerSocket* socket = [[Cst420ServerSocket alloc] initWithPort: clientPort];
[socket retain];
NSString* returnString;
while(YES) {
printf("Client waiting for callbacks on port %s\n", [clientPort cString]);
if([socket accept]) {
printf("Connection accepted!\n");
while(YES) {
printf("Inner loop\n");
sleep(1);
returnString = [socket receiveBytes: buffer maxBytes: MAXDATASIZE beginAt: 0];
printf("Received from Server |%s|\n", [returnString cString]);
if([returnString length] > 0) {
printf("Got a callback from server\n");
[appDelegate populateGui];
}
printf("Going to sleep now\n");
sleep(1);
}
[socket close];
}
}
}
#end
Cst420ServerSocket has been provided to us by the instructor. It looks like this:
#import "Cst420Socket.h"
#define PORT "4444"
/**
* Cst420Socket.m - objective-c class for manipulating stream sockets.
* Purpose: demonstrate stream sockets in Objective-C.
* These examples are buildable on MacOSX and GNUstep on top of Windows7
*/
// get sockaddr, IPv4 or IPv6:
void *get_in_addr(struct sockaddr *sa){
if (sa->sa_family == AF_INET) {
return &(((struct sockaddr_in*)sa)->sin_addr);
}
return &(((struct sockaddr_in6*)sa)->sin6_addr);
}
#implementation Cst420ServerSocket
- (id) initWithPort: (NSString*) port{
self = [super init];
int ret = 0;
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE; // use my IP
const char* portStr = [port UTF8String];
if ((rv = getaddrinfo(NULL, portStr, &hints, &servinfo)) != 0) {
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
ret = 1;
}else{
for(p = servinfo; p != NULL; p = p->ai_next) {
if ((sockfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP))==-1){
perror("server: socket create error");
continue;
}
if (bind(sockfd, p->ai_addr, p->ai_addrlen) == -1) {
#if defined(WINGS)
closesocket(sockfd);
#else
close(sockfd);
#endif
perror("server: bind error");
continue;
}
break;
}
if (p == NULL) {
fprintf(stderr, "server: failed to bind\n");
ret = 2;
}else{
freeaddrinfo(servinfo); // all done with this structure
if (listen(sockfd, BACKLOG) == -1) {
perror("server: listen error");
ret = 3;
}
}
if (ret == 0){
return self;
} else {
return nil;
}
}
}
- (BOOL) accept {
BOOL ret = YES;
#if defined(WINGS)
new_fd = accept(sockfd, NULL, NULL);
#else
new_fd = accept(sockfd, (struct sockaddr *)&their_addr, &sin_size);
#endif
if (new_fd == -1) {
perror("server: accept error");
ret = NO;
}
connected = ret;
return ret;
}
- (int) sendBytes: (char*) byteMsg OfLength: (int) msgLength Index: (int) at{
int ret = send(new_fd, byteMsg, msgLength, 0);
if(ret == -1){
NSLog(#"error sending bytes");
}
return ret;
}
- (NSString* ) receiveBytes: (char*) byteMsg
maxBytes: (int) max
beginAt: (int) at {
int ret = recv(new_fd, byteMsg, max-1, at);
if(ret == -1){
NSLog(#"server error receiving bytes");
}
byteMsg[ret+at] = '\0';
NSString * retStr = [NSString stringWithUTF8String: byteMsg];
return retStr;
}
- (BOOL) close{
#if defined(WINGS)
closesocket(new_fd);
#else
close(new_fd);
#endif
connected = NO;
return YES;
}
- (void) dealloc {
#if defined(WINGS)
closesocket(sockfd);
#else
close(sockfd);
#endif
[super dealloc];
}
#end
Our professor also provided us an example of a simple echo server and client (the server just spits back whatever the client sent it) and I've used the same pattern in the thread.
My initial problem was that my callback interceptor thread didn't accept any (callback) connections from the server. The server said that it could not connect back to the client (ConnectException from Java; it said "Connection refused"). I was able to fix this by changing my instructor's code. In the connect function (not shown), he had set the hints to use AF_UNSPEC instead of AF_INET. So Java was seeing my localhost IP come through as 0:0:0:0:0:0:0:1 (in IPv6 format). When Java tried to connect back to send a callback, it received an exception (not sure why it cannot connect to an IPv6 address).
After fixing this problem, I tried out my app again and this time the callback from the server was received by my client. However, subsequent callbacks fail to work. After receiving the first callback, the busy-loop keeps running (as it should). But when the server sends a second callback, it looks like the client cannot read it in. On the server side I can see that it sent the callback to the client successfully. It's just that the client is having trouble reading in the data. I added some print statements (see above) for debugging and this is what I get:
Client waiting for callbacks on port 2020
Connection accepted!
Inner loop
Received from Server |A callback from server to 127.0.0.1:2020|
Got a callback from server
Going to sleep now
Inner loop
Received from Server ||
Going to sleep now
Inner loop
Received from Server ||
Going to sleep now
Inner loop
... (and it keeps going regardless of the second callback being sent)
Here is how I am starting the thread (from the GUI):
CallbackInterceptorThread* callbackInterceptorThread = [[CallbackInterceptorThread alloc] initWithClientPort: clientPort appDelegate: self];
[callbackInterceptorThread start];
I think I've got it working. So from the Java side (the server), this was what I was doing:
Socket socket = new Socket(clientAddress, clientPort);
BufferedOutputStream out = new BufferedOutputStream(socket.getOutputStream());
out.write(("A callback from server to " + clientAddress + ":" + clientPort).getBytes());
out.flush();
out.close();
I put some debugging print-statements in my professor's code and noticed that in receiveBytes, recv was returning 0. The return value of recv is the length of the message that it received. So it received a zero-length string. But a return value of 0 also means that the peer closed the connection properly (which is exactly what I had done from the Java side with out.close()). So I figured that if I needed to respond to the second callback, I would need to accept the connection again. So I changed my busy loop to this:
printf("Client waiting for callbacks on port %s\n", [clientPort cString]);
while([socket accept]) {
printf("Connection accepted!\n");
returnString = [socket receiveBytes: buffer maxBytes: MAXDATASIZE beginAt: 0];
printf("Received from Server |%s|\n", [returnString cString]);
if([returnString length] > 0) {
printf("Got a callback from server\n");
[appDelegate populateGui];
}
}
[socket close];
and that seemed to do the trick. I am not sure if this is the right way to do it, so I am open to suggestions for improvement!

CoreMidi.framework sending midi commands

there is an app called FreeStyler, that you can control using midi commands. In my mac app I want to send midi signals.
Can someone show an example of this?
Elijah
This is what it took to send a note to my blofeld synth. I hope it helps. You can use MIDIObjectGetProperties to find the uniqueIDs for all the midi devices connected to your mac.
#import <Foundation/Foundation.h>
#import <CoreMIDI/CoreMIDI.h>
MIDIEndpointRef getEndpointWithUniqueID(MIDIUniqueID id){
MIDIObjectRef endPoint;
MIDIObjectType foundObj;
MIDIObjectFindByUniqueID(id, &endPoint, &foundObj);
return (MIDIEndpointRef) endPoint;
}
MIDIClientRef getMidiClient(){
MIDIClientRef midiClient;
NSString *outPortName =#"blofeldOut";
MIDIClientCreate((CFStringRef)outPortName, NULL, NULL, &midiClient);
return midiClient;
}
MIDIPortRef getOutPutPort(){
MIDIPortRef outPort;
NSString *outPortName =#"blofeldOut";
MIDIOutputPortCreate(getMidiClient(), (CFStringRef)outPortName, &outPort);
return outPort;
}
MIDIPacketList getMidiPacketList(){
MIDIPacketList packetList;
packetList.numPackets = 1;
MIDIPacket* firstPacket = &packetList.packet[0];
firstPacket->timeStamp = 0; // send immediately
firstPacket->length = 3;
firstPacket->data[0] = 0x90;
firstPacket->data[1] = 60;
firstPacket->data[2] = 64;
// TODO: add end note sequence
return packetList;
}
void play_note(void) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
MIDIPacketList packetList=getMidiPacketList();
MIDIUniqueID blofeldEndpointID = -934632258;
MIDIEndpointRef blofeldEndpoint = getEndpointWithUniqueID(blofeldEndpointID);
MIDISend(getOutPutPort(), blofeldEndpoint, &packetList);
MIDIEndpointDispose(blofeldEndpoint);
[pool drain];
}
int main (int argc, const char * argv[]) {
play_note();
return 0;
}
Your application will need to use the CoreMIDI framework to send or receive MIDI, which I can tell you from experience is not a lot of fun to work with directly. You might want to try the vvopensource framework, which is a MIDI framework designed for cocoa.