diff --git a/Makefile b/Makefile
index b6a1f4bc3ab354be6952dfd5fc39ad5f4305c32b..55b4962c4282e293edae52ab5dcb8ebdc4f505cf 100644
--- a/Makefile
+++ b/Makefile
@@ -1,6 +1,6 @@
 DOTNET = xxdk.NET
 
-.PHONY: all windows-x64 windows-arm64 linux-arm64 linux-arm64 darwin-x64 darwin-arm64 dotnet
+.PHONY: all windows-x64 windows-arm64 linux-x64 linux-arm64 darwin-x64 darwin-arm64 dotnet
 
 libxxdk-win-x64.dll:
 	CGO_ENABLED=1 GOOS=windows GOARCH=amd64 go build -buildmode=c-shared -o $@ ./sharedcgo
diff --git a/README.md b/README.md
index a2baedb577ff63e89219faf0c666978f3880f271..b7800183dd58d5a98f68e90d805077d546ae68e5 100644
--- a/README.md
+++ b/README.md
@@ -9,11 +9,30 @@ x86_64). It should run on Windows but may need the library name
 changed.
 
 ```
-make -k
+make linux-x64
 cd xxdk.NET
 dotnet run --ndf mainnet-ndf.json --state-dir world --wait 20 | grep ^DM
 ```
 
+Usually, you will want to make the library only for your
+architecture. The options are:
+
+* windows-x64
+* windows-arm64
+* linux-x64
+* linux-arm64
+* darwin-x64
+* darwin-arm64
+
+NOTE: you may need to specify a compiler to the make command, especially
+when cross compiling. Example for compiling to windows:
+```
+CC=x86_64-w64-mingw32-gcc make windows-x64
+cd xxdk.NET
+dotnet run --ndf mainnet-ndf.json --state-dir world --wait 20 | grep ^DM
+```
+
+
 It's highly recommended to run with grep on the output. Especially on
 mainnet, logs can be noisy with failure to connect errors as it accesses
 the network.
diff --git a/sharedcgo/callbacks.go b/sharedcgo/callbacks.go
index d393f5e55105f1a7a15399a25d61c2e6490175ba..276f5672dcc88d57e75bbbfd036fcad9e63a98d6 100644
--- a/sharedcgo/callbacks.go
+++ b/sharedcgo/callbacks.go
@@ -21,7 +21,7 @@ package main
 // // here because of cgo rules.                                              //
 // /////////////////////////////////////////////////////////////////////////////
 //
-// long cmix_dm_receive(int dm_instance_id,
+// DLL_EXPORT long cmix_dm_receive(int dm_instance_id,
 //    void* message_id, int message_id_len,
 //    char* nickname, int nickname_len,
 //    void* text, int text_len,
@@ -34,7 +34,7 @@ package main
 //       text, text_len, partnerkey, partnerkey_len, senderkey, senderkey_len,
 //       dmToken, codeset, timestamp, round_id, msg_type, status);
 // }
-// long cmix_dm_receive_text(int dm_instance_id,
+// DLL_EXPORT long cmix_dm_receive_text(int dm_instance_id,
 //    void* message_id, int message_id_len,
 //    char* nickname, int nickname_len,
 //    char* text, int text_len,
@@ -47,7 +47,7 @@ package main
 //       text, text_len, partnerkey, partnerkey_len, senderkey, senderkey_len,
 //       dmToken, codeset, timestamp, round_id, status);
 // }
-// long cmix_dm_receive_reply(int dm_instance_id,
+// DLL_EXPORT long cmix_dm_receive_reply(int dm_instance_id,
 //    void* message_id, int message_id_len,
 //    void* reply_to, int reply_to_len,
 //    char* nickname, int nickname_len,
@@ -62,7 +62,7 @@ package main
 //       text, text_len, partnerkey, partnerkey_len, senderkey, senderkey_len,
 //       dmToken, codeset, timestamp, round_id, status);
 // }
-// long cmix_dm_receive_reaction(int dm_instance_id,
+// DLL_EXPORT long cmix_dm_receive_reaction(int dm_instance_id,
 //    void* message_id, int message_id_len,
 //    void* reaction_to, int reaction_to_len,
 //    char* nickname, int nickname_len,
@@ -77,17 +77,17 @@ package main
 //       text, text_len, partnerkey, partnerkey_len, senderkey, senderkey_len,
 //       dmToken, codeset, timestamp, round_id, status);
 // }
-// void cmix_dm_update_sent_status(int dm_instance_id,
+// DLL_EXPORT void cmix_dm_update_sent_status(int dm_instance_id,
 //    long uuid,
 //    void* message_id, int message_id_len, long timestamp,
 //    long round_id, long status) {
 //    DMReceiverRouter.updateSentStatusFn(dm_instance_id, uuid,
 //        message_id, message_id_len, timestamp, round_id, status);
 // }
-// void cmix_dm_block_sender(int dm_instance_id, void* pubkey, int pubkey_len) {
+// DLL_EXPORT void cmix_dm_block_sender(int dm_instance_id, void* pubkey, int pubkey_len) {
 //    DMReceiverRouter.blockSenderFn(dm_instance_id, pubkey, pubkey_len);
 // }
-// void cmix_dm_unblock_sender(int dm_instance_id, void* pubkey,
+// DLL_EXPORT void cmix_dm_unblock_sender(int dm_instance_id, void* pubkey,
 //    int pubkey_len) {
 //    DMReceiverRouter.unblockSenderFn(dm_instance_id, pubkey, pubkey_len);
 // }
@@ -99,7 +99,7 @@ package main
 // GoByteSlice cmix_dm_get_conversations(int dm_instance_id) {
 //    return DMReceiverRouter.getConversationsFn(dm_instance_id);
 // }
-// extern void cmix_dm_set_router(DMReceiverRouterFunctions cbs) {
+// DLL_EXPORT void cmix_dm_set_router(DMReceiverRouterFunctions cbs) {
 //     DMReceiverRouter.receiveFn = cbs.receiveFn;
 //     DMReceiverRouter.receiveTextFn = cbs.receiveTextFn;
 //     DMReceiverRouter.receiveReplyFn = cbs.receiveReplyFn;
diff --git a/sharedcgo/callbacks.h b/sharedcgo/callbacks.h
index a63866e7131def52a88332a07e33220b6d7862ea..043ac00e6b1d1326b49840a53ea624d2a7be095f 100644
--- a/sharedcgo/callbacks.h
+++ b/sharedcgo/callbacks.h
@@ -72,5 +72,10 @@ typedef struct {
   cmix_dm_get_conversations_fn getConversationsFn;
 } DMReceiverRouterFunctions;
 
+#ifdef _WIN32
+#define DLL_EXPORT __declspec(dllexport)
+#else
+#define DLL_EXPORT
+#endif
 
 #endif