I found and read this question but I didn't found my answer SSDT hooking alternative in x64 systems
I want to protect my application against termination by other programs. In the 32Bit version of windows I used the SSDT hooking for hooking ZwTerminateProcess or ZwOpenProcess. I've to upgrade my program to using in 64Bit version of windows now.
And unfortunately in the 64bit windows we can't use SSDT hook (Because Patch Guard (KPP)), Notice that I don't want to Bypassing PG in this case and I've to use only kernel-mode hooking. For example, I don't want to my program begin terminated (Even )by the following code :
NTSTATUS drvTerminateProcess( ULONG ulProcessID )
{
NTSTATUS ntStatus = STATUS_SUCCESS;
HANDLE hProcess;
OBJECT_ATTRIBUTES ObjectAttributes;
CLIENT_ID ClientId;
DbgPrint( "drvTerminateProcess( %u )", ulProcessID );
InitializeObjectAttributes( &ObjectAttributes, NULL, OBJ_INHERIT, NULL, NULL );
ClientId.UniqueProcess = (HANDLE)ulProcessID;
ClientId.UniqueThread = NULL;
__try
{
ntStatus = ZwOpenProcess( &hProcess, PROCESS_ALL_ACCESS, &ObjectAttributes, &ClientId );
if( NT_SUCCESS(ntStatus) )
{
ntStatus = ZwTerminateProcess( hProcess, 0 );
if( !NT_SUCCESS(ntStatus) )
DbgPrint( "ZwTerminateProcess failed with status : %08X\n", ntStatus );
ZwClose( hProcess );
}
else
DbgPrint( "ZwOpenProcess failed with status : %08X\n", ntStatus );
}
__except( EXCEPTION_EXECUTE_HANDLER )
{
ntStatus = STATUS_UNSUCCESSFUL;
DbgPrint( "Exception caught in drvTerminateProcess()" );
}
return ntStatus;
}
To do this work I used the following function (NewZwOpenProcess) and replace it with the original ZwOpenProcess in SSDT but in x64 windows I don't know what should I do :( :
NTSTATUS NewZwOpenProcess(
OUT PHANDLE ProcessHandle,
IN ACCESS_MASK DesiredAccess,
IN POBJECT_ATTRIBUTES ObjectAttributes,
IN PCLIENT_ID ClientId OPTIONAL)
{
HANDLE ProcessId;
__try
{
ProcessId = ClientId->UniqueProcess;
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
return STATUS_INVALID_PARAMETER;
}
if (ProcessId == (HANDLE)11) //Check if the PID matches our protected process PID (My programm)
{
return STATUS_ACCESS_DENIED;
}
else
return OldZwOpenProcess(ProcessHandle, DesiredAccess,ObjectAttributes, ClientId);
}
Any idea ??
(Excuse me if my English is bad )
I found my answer, I use kernel mode callbacks .
#include <ntddk.h>
#include <common.h>
// coded by Behrooz
VOID UnloadRoutine(IN PDRIVER_OBJECT DriverObject)
{
FreeProcFilter();
DbgPrintEx( DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL,"Unloaded\n");
}
NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath)
{
NTSTATUS status = RegisterCallbackFunction();
if(!NT_SUCCESS(status))
{
DbgPrintEx( DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL,"Faild to RegisterCallbackFunction .status : 0x%X \n",status);
}
DriverObject->DriverUnload = UnloadRoutine;
DbgPrintEx( DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL,"Driver Loaded\n");
return STATUS_SUCCESS;
}
//
// PRE OPERATION
//
OB_PREOP_CALLBACK_STATUS ObjectPreCallback(
IN PVOID RegistrationContext,
IN POB_PRE_OPERATION_INFORMATION OperationInformation
)
{
LPSTR ProcName;
// OB_PRE_OPERATION_INFORMATION OpInfo;
UNREFERENCED_PARAMETER(RegistrationContext);
ProcName=GetProcessNameFromPid(PsGetProcessId((PEPROCESS)OperationInformation->Object));
if( !_stricmp(ProcName,"calc.exe") )
{
if (OperationInformation->Operation == OB_OPERATION_HANDLE_CREATE)
{
if ((OperationInformation->Parameters->CreateHandleInformation.OriginalDesiredAccess & PROCESS_TERMINATE) == PROCESS_TERMINATE)
{
OperationInformation->Parameters->CreateHandleInformation.DesiredAccess &= ~PROCESS_TERMINATE;
}
if ((OperationInformation->Parameters->CreateHandleInformation.OriginalDesiredAccess & PROCESS_VM_OPERATION) == PROCESS_VM_OPERATION)
{
OperationInformation->Parameters->CreateHandleInformation.DesiredAccess &= ~PROCESS_VM_OPERATION;
}
if ((OperationInformation->Parameters->CreateHandleInformation.OriginalDesiredAccess & ~PROCESS_VM_READ) == PROCESS_VM_READ)
{
OperationInformation->Parameters->CreateHandleInformation.DesiredAccess &= ~PROCESS_VM_READ;
}
if ((OperationInformation->Parameters->CreateHandleInformation.OriginalDesiredAccess & PROCESS_VM_WRITE) == PROCESS_VM_WRITE)
{
OperationInformation->Parameters->CreateHandleInformation.DesiredAccess &= ~PROCESS_VM_WRITE;
}
}
}
DbgPrintEx( DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL,"ObjectPreCallback ----> Process Name [%s] \n", ProcName);
return OB_PREOP_SUCCESS;
}
//
//POST OPERATION
//
VOID ObjectPostCallback(
IN PVOID RegistrationContext,
IN POB_POST_OPERATION_INFORMATION OperationInformation
)
{
DbgPrintEx( DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL,"PostProcCreateRoutine. \n");
}
//
// REGISTE CALLBACK FUNCTION
//
NTSTATUS RegisterCallbackFunction()
{
NTSTATUS ntStatus = STATUS_SUCCESS;
UNICODE_STRING Altitude;
USHORT filterVersion = ObGetFilterVersion();
USHORT registrationCount = 1;
OB_OPERATION_REGISTRATION RegisterOperation;
OB_CALLBACK_REGISTRATION RegisterCallBack;
REG_CONTEXT RegistrationContext;
memset(&RegisterOperation, 0, sizeof(OB_OPERATION_REGISTRATION));
memset(&RegisterCallBack, 0, sizeof(OB_CALLBACK_REGISTRATION));
memset(&RegistrationContext, 0, sizeof(REG_CONTEXT));
RegistrationContext.ulIndex = 1;
RegistrationContext.Version = 120;
if (filterVersion == OB_FLT_REGISTRATION_VERSION) {
DbgPrintEx( DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL,"Filter Version is correct.\n");
RegisterOperation.ObjectType = PsProcessType;
RegisterOperation.Operations = OB_OPERATION_HANDLE_CREATE;
RegisterOperation.PreOperation = ObjectPreCallback;
RegisterOperation.PostOperation = ObjectPostCallback;
RegisterCallBack.Version = OB_FLT_REGISTRATION_VERSION;
RegisterCallBack.OperationRegistrationCount = registrationCount;
RtlInitUnicodeString(&Altitude, L"XXXXXXX");
RegisterCallBack.Altitude = Altitude;
RegisterCallBack.RegistrationContext = &RegistrationContext;
RegisterCallBack.OperationRegistration = &RegisterOperation;
DbgPrintEx( DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL,"Register Callback Function Entry.\n");
ntStatus = ObRegisterCallbacks(&RegisterCallBack, &_CallBacks_Handle);
if (ntStatus == STATUS_SUCCESS) {
DbgPrintEx( DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL,"Register Callback Function Successful.\n");
}
else {
if (ntStatus == STATUS_FLT_INSTANCE_ALTITUDE_COLLISION) {
DbgPrintEx( DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL,"Status Filter Instance Altitude Collision.\n");
}
if (ntStatus == STATUS_INVALID_PARAMETER) {
DbgPrintEx( DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL,"Status Invalid Parameter.\n");
}
if (ntStatus == STATUS_ACCESS_DENIED) {
DbgPrintEx( DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL,"The callback routines do not reside in a signed kernel binary image.\n");
}
if (ntStatus == STATUS_INSUFFICIENT_RESOURCES) {
DbgPrintEx( DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL,"Status Allocate Memory Failed.\n");
}
DbgPrintEx( DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL,"Register Callback Function Failed with 0x%08x\n",ntStatus);
}
}
else {
DbgPrintEx( DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL,"Filter Version is not supported.\n");
}
return ntStatus;
}
//
// FREE PROC FILTER
//
NTSTATUS FreeProcFilter()
{
// if the callbacks are active - remove them
if (NULL != _CallBacks_Handle)
{
ObUnRegisterCallbacks(_CallBacks_Handle);
_CallBacks_Handle=NULL;
}
return STATUS_SUCCESS;
}
LPSTR GetProcessNameFromPid(HANDLE pid)
{
PEPROCESS Process;
if (PsLookupProcessByProcessId(pid, & Process) == STATUS_INVALID_PARAMETER)
{
return "pid???";
}
return (LPSTR)PsGetProcessImageFileName(Process);
}
common.h
#include <ntddk.h>
// coded by Behrooz
//-----------------------------------------------
// Defines
//-----------------------------------------------
//Process Security and Access Rights
#define PROCESS_CREATE_THREAD (0x0002)
#define PROCESS_CREATE_PROCESS (0x0080)
#define PROCESS_TERMINATE (0x0001)
#define PROCESS_VM_WRITE (0x0020)
#define PROCESS_VM_READ (0x0010)
#define PROCESS_VM_OPERATION (0x0008)
#define PROCESS_SUSPEND_RESUME (0x0800)
#define MAXIMUM_FILENAME_LENGTH 256
//-----------------------------------------------
// callback
//-----------------------------------------------
PVOID _CallBacks_Handle = NULL;
typedef struct _OB_REG_CONTEXT {
__in USHORT Version;
__in UNICODE_STRING Altitude;
__in USHORT ulIndex;
OB_OPERATION_REGISTRATION *OperationRegistration;
} REG_CONTEXT, *PREG_CONTEXT;
//-----------------------------------------------
// PID2ProcName
//-----------------------------------------------
extern UCHAR *PsGetProcessImageFileName(IN PEPROCESS Process);
extern NTSTATUS PsLookupProcessByProcessId(
HANDLE ProcessId,
PEPROCESS *Process
);
typedef PCHAR (*GET_PROCESS_IMAGE_NAME) (PEPROCESS Process);
GET_PROCESS_IMAGE_NAME gGetProcessImageFileName;
LPSTR GetProcessNameFromPid(HANDLE pid);
//-----------------------------------------------
// Forward Declaration
//-----------------------------------------------
NTSTATUS DriverEntry(
IN PDRIVER_OBJECT DriverObject,
IN PUNICODE_STRING RegistryPath
);
VOID UnloadDriver(
IN PDRIVER_OBJECT DriverObject
);
OB_PREOP_CALLBACK_STATUS ObjectPreCallback(
IN PVOID RegistrationContext,
IN POB_PRE_OPERATION_INFORMATION OperationInformation
);
VOID ObjectPostCallback(
IN PVOID RegistrationContext,
IN POB_POST_OPERATION_INFORMATION OperationInformation
);
NTSTATUS RegisterCallbackFunction() ;
NTSTATUS FreeProcFilter();
The result of my test:
Thanks for you code, and I test it on win10 x64 success.
BTW, I was found the ObRegisterCallbacks return STATUS_ACCESS_DENIED even though I have signed my driver.
So I asks many peoples, and then I find a solution for it.
Append this text in you "source" file:
LINKER_FLAGS=/integritycheck
then you can rebuild and resign your driver. It was found in bbs.pediy.com
Someone can do "ObUnregisterCallbacks" and even register their own to prevent you from detecting it.
Looks like .inf file is not needed. just SC CREATE ServiceName type= kernel binPath= pathtoyourfile.
Related
I'm writing a Napi-based module for Node.js.
The module uses WINAPI WaitForSingleObject(pid). The blocking WINAPI call is wrapped in Napi::AsyncWorker.
The issue
The async call prevents Node.js from exiting. I want Node.js to exit when it has nothing else to do, like it does with child_process.unref(). So I want to unref the async call from the Node.js event loop.
I don't have time to make a NPM package of it, but here is my solution. The solution is valid for all blocking system calls (that put the calling thread to the waiting state, like Sleep() does).
The idea is to:
Use std::thread for the blocking WINAPI call.
Use napi_threadsafe_function to call back from the new thread to Javascript safely.
Use napi_unref_threadsafe_function to unref the async operation from Node.js event loop.
More details:
For safe simultaneous calls, create a new void* context with thread-specific data and pass it around (supported by N-API).
For the unreffed napi_threadsafe_function, finalizer is supported. Stop the waiting thread here to prevent crash messages upon Node.js exit.
I use the C N-API and C++ node-addon-api interchangeably. Unfortunately a pure C++ solution is impossible at the time of writing because napi_unref_threadsafe_function is only available from the C API.
addon.cc:
#include <napi.h>
#include <node_api.h>
#include <windows.h>
#include <tlhelp32.h>
#include <sstream>
#include <string>
#include <iostream>
#include <thread>
using namespace std;
using namespace Napi;
struct my_context {
std::thread nativeThread;
HANDLE hStopEvent;
napi_threadsafe_function tsfn;
};
std::string WaitForPid(int pid, my_context* context, bool* stopped) {
HANDLE process = OpenProcess(SYNCHRONIZE, FALSE, pid);
if (process == NULL)
{
std::stringstream stream;
stream << "OpenProcess failed: " << (int)GetLastError();
return stream.str();
}
context->hStopEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
if (process == NULL)
{
std::stringstream stream;
stream << "CreateEvent failed: " << (int)GetLastError();
return stream.str();
}
HANDLE hEvents[2] = {process, context->hStopEvent};
DWORD waitResult = WaitForMultipleObjects(2, hEvents, FALSE, INFINITE);
if (waitResult == WAIT_FAILED)
{
std::stringstream stream;
stream << "WaitForSingleObject failed: " << (int)GetLastError();
return stream.str();
} else if (waitResult == WAIT_OBJECT_0 + 1) {
*stopped = true;
}
return std::string();
}
Value WaitForProcessToExit(CallbackInfo &info)
{
Env env = info.Env();
if (info.Length() < 3) {
TypeError::New(env, "Wrong number of arguments").ThrowAsJavaScriptException();
return env.Null();
}
if (!info[0].IsNumber() || !info[1].IsBoolean() || !info[2].IsFunction()) {
TypeError::New(env, "Wrong arguments").ThrowAsJavaScriptException();
return env.Null();
}
int pid = info[0].As<Number>().Int32Value();
bool unref = info[1].As<Boolean>();
Function func = info[2].As<Function>();
// Do async stuff
my_context* context = new my_context();
NAPI_THROW_IF_FAILED(env, napi_create_threadsafe_function(
(napi_env) env,
(napi_value) func,
NULL,
(napi_value) String::New(env, "WaitForProcessToExit"),
0,
1,
NULL,
[](napi_env env, void* finalize_data, void* finalize_hint /* Context attached to the TSFN */) {
SetEvent(((my_context*)finalize_hint)->hStopEvent);
((my_context*)finalize_hint)->nativeThread.join();
delete finalize_hint;
},
context, // Context attached to the TSFN
[](napi_env env, napi_value js_callback, void* context, void* data) {
std::string* error = (std::string*)data;
// Transform native data into JS data, passing it to the provided `js_callback` (the TSFN's JavaScript function)
napi_status status = napi_ok;
napi_value global;
status = napi_get_global(env, &global);
if (status != napi_ok)
{
std::cout << "napi_get_global failed" << std::endl;
}
napi_value result;
if (error->empty()) {
status = napi_call_function(env, global, js_callback, 0, NULL, &result);
} else {
napi_value values[] = { (napi_value)Error::New(env, *error).Value() };
status = napi_call_function(env, global, js_callback, 1, values, &result);
}
delete data;
if (status != napi_ok)
{
std::cout << "napi_call_function failed" << std::endl;
}
status = napi_release_threadsafe_function(((my_context*)context)->tsfn, napi_tsfn_release);
if (status != napi_ok)
{
std::cout << "napi_release_threadsafe_function failed" << std::endl;
}
},
&(context->tsfn)), env.Undefined());
context->nativeThread = std::thread([pid, context] {
bool stopped = false;
std::string error = WaitForPid(pid, context, &stopped);
if (stopped) {
return;
}
napi_status status = napi_call_threadsafe_function(context->tsfn, new std::string(error), napi_tsfn_blocking);
if (status != napi_ok)
{
std::cout << "napi_call_threadsafe_function failed" << std::endl;
}
});
if (unref) {
NAPI_THROW_IF_FAILED(env, napi_unref_threadsafe_function((napi_env) env, context->tsfn), env.Undefined());
}
return env.Undefined();
}
Object Init(Env env, Object exports)
{
exports.Set(String::New(env, "WaitForProcessToExit"), Function::New(env, Winapi::WaitForProcessToExit));
return exports;
}
NODE_API_MODULE(hello, Init)
I'm using code from An Introduction to Bluetooth Programming ► Chapter 4. Bluetooth programming in C with BlueZ4.2. ► RFCOMM sockets to send messages between two Raspberry Pi.
However, if I don't make the pairing between two devices through the bluetoothctl, I can't use the client because it gives me error:
uh oh: Invalid exchange.
Can you give me some hints on how can I make the pair through the C code? I need to use this "automatically" without need to pairing through the bluetoothctl before the C code.
Before getting into my answer, I am not sure how to achieve this using "libbluetooth" API's. But my below answer is based on DBUS API using GDBUS. This should most likely work with any recent bluez (with bluetoothd) running.
Note, with Bluez5 it's recommended to use DBUS API's.
To brief, you need to develop an Agent which accepts the Pairing request automatically, assuming "Confirmation" agent here. Please refer agent capabilities here.
With recent bluez version (atleast 5.47+), we have a new API "ConnectDevice" which can be used to connect device without scanning/discovery. From your question, I understand that you are trying to communicate between two RPi's, so you can find the BT address for both the bluetooth controllers. With BT address in places,
/*
* bluez_adapter_connect.c - Connect with device without StartDiscovery
* - This example registers an agen with NoInputOutput capability for the purpose of
* auto pairing
* - Use ConnectDevice method to connect with device using provided MAC address
* - Usual signal subscription to get the details of the connected device
* - Introduced new signal handler to exit the program gracefully
*
* Note: As "ConnectDevice" is new API and in experimental state (but mostly stable)
* one need to use "-E" option when starting "bluetoothd". Systems running systemd can
* edit /lib/systemd/system/bluetooth.service in ExecStart option
*
* When this API is useful?
* - When you already have the MAC address of end bluetooth Device to connect with, then
* you don't need to scan for the device (with or without filter) and connect it.
* - StartDiscovery + Pair + Connect => ConnectDevice
*
* How you will have MAC address before scanning?
* - When you have other communication (wired or wireless) medium to exchange the MAC address
* - For example, NFC OOB can be used to exchange the MAC address
* - Testing Bluetooth with same device (MAC address known)
*
* - Here Agent capability is registered as "NoInputOutput" for experimental purpose only, in
* real world scenario, Pair + Connect involves real Agents.
* - Also note, bluez_agent_call_method and bluez_adapter_call_method are two different methods doing
* the same work with difference in interface name and object path. This exist just to make the
* understanding more clear.
*
* gcc `pkg-config --cflags glib-2.0 gio-2.0` -Wall -Wextra -o ./bin/bluez_adapter_connect ./bluez_adapter_connect.c `pkg-config --libs glib-2.0 gio-2.0`
*/
#include <glib.h>
#include <gio/gio.h>
#include <signal.h>
GMainLoop *loop;
GDBusConnection *con;
static void bluez_property_value(const gchar *key, GVariant *value)
{
const gchar *type = g_variant_get_type_string(value);
g_print("\t%s : ", key);
switch(*type) {
case 'o':
case 's':
g_print("%s\n", g_variant_get_string(value, NULL));
break;
case 'b':
g_print("%d\n", g_variant_get_boolean(value));
break;
case 'u':
g_print("%d\n", g_variant_get_uint32(value));
break;
case 'a':
/* TODO Handling only 'as', but not array of dicts */
if(g_strcmp0(type, "as"))
break;
g_print("\n");
const gchar *uuid;
GVariantIter i;
g_variant_iter_init(&i, value);
while(g_variant_iter_next(&i, "s", &uuid))
g_print("\t\t%s\n", uuid);
break;
default:
g_print("Other\n");
break;
}
}
typedef void (*method_cb_t)(GObject *, GAsyncResult *, gpointer);
static int bluez_adapter_call_method(const char *method, GVariant *param, method_cb_t method_cb)
{
g_dbus_connection_call(con,
"org.bluez",
/* TODO Find the adapter path runtime */
"/org/bluez/hci0",
"org.bluez.Adapter1",
method,
param,
NULL,
G_DBUS_CALL_FLAGS_NONE,
-1,
NULL,
method_cb,
(void *)method);
return 0;
}
static void bluez_result_async_cb(GObject *con,
GAsyncResult *res,
gpointer data)
{
const gchar *key = (gchar *)data;
GVariant *result = NULL;
GError *error = NULL;
result = g_dbus_connection_call_finish((GDBusConnection *)con, res, &error);
if(error != NULL) {
g_print("Unable to get result: %s\n", error->message);
return;
}
if(result) {
result = g_variant_get_child_value(result, 0);
bluez_property_value(key, result);
}
g_variant_unref(result);
}
static void bluez_device_appeared(GDBusConnection *sig,
const gchar *sender_name,
const gchar *object_path,
const gchar *interface,
const gchar *signal_name,
GVariant *parameters,
gpointer user_data)
{
(void)sig;
(void)sender_name;
(void)object_path;
(void)interface;
(void)signal_name;
(void)user_data;
GVariantIter *interfaces;
const char *object;
const gchar *interface_name;
GVariant *properties;
g_variant_get(parameters, "(&oa{sa{sv}})", &object, &interfaces);
while(g_variant_iter_next(interfaces, "{&s#a{sv}}", &interface_name, &properties)) {
if(g_strstr_len(g_ascii_strdown(interface_name, -1), -1, "device")) {
g_print("[ %s ]\n", object);
const gchar *property_name;
GVariantIter i;
GVariant *prop_val;
g_variant_iter_init(&i, properties);
while(g_variant_iter_next(&i, "{&sv}", &property_name, &prop_val))
bluez_property_value(property_name, prop_val);
g_variant_unref(prop_val);
}
g_variant_unref(properties);
}
return;
}
#define BT_ADDRESS_STRING_SIZE 18
static void bluez_device_disappeared(GDBusConnection *sig,
const gchar *sender_name,
const gchar *object_path,
const gchar *interface,
const gchar *signal_name,
GVariant *parameters,
gpointer user_data)
{
(void)sig;
(void)sender_name;
(void)object_path;
(void)interface;
(void)signal_name;
GVariantIter *interfaces;
const char *object;
const gchar *interface_name;
char address[BT_ADDRESS_STRING_SIZE] = {'\0'};
g_variant_get(parameters, "(&oas)", &object, &interfaces);
while(g_variant_iter_next(interfaces, "s", &interface_name)) {
if(g_strstr_len(g_ascii_strdown(interface_name, -1), -1, "device")) {
int i;
char *tmp = g_strstr_len(object, -1, "dev_") + 4;
for(i = 0; *tmp != '\0'; i++, tmp++) {
if(*tmp == '_') {
address[i] = ':';
continue;
}
address[i] = *tmp;
}
g_print("\nDevice %s removed\n", address);
g_main_loop_quit((GMainLoop *)user_data);
}
}
return;
}
static void bluez_signal_adapter_changed(GDBusConnection *conn,
const gchar *sender,
const gchar *path,
const gchar *interface,
const gchar *signal,
GVariant *params,
void *userdata)
{
(void)conn;
(void)sender;
(void)path;
(void)interface;
(void)userdata;
GVariantIter *properties = NULL;
GVariantIter *unknown = NULL;
const char *iface;
const char *key;
GVariant *value = NULL;
const gchar *signature = g_variant_get_type_string(params);
if(strcmp(signature, "(sa{sv}as)") != 0) {
g_print("Invalid signature for %s: %s != %s", signal, signature, "(sa{sv}as)");
goto done;
}
g_variant_get(params, "(&sa{sv}as)", &iface, &properties, &unknown);
while(g_variant_iter_next(properties, "{&sv}", &key, &value)) {
if(!g_strcmp0(key, "Powered")) {
if(!g_variant_is_of_type(value, G_VARIANT_TYPE_BOOLEAN)) {
g_print("Invalid argument type for %s: %s != %s", key,
g_variant_get_type_string(value), "b");
goto done;
}
g_print("Adapter is Powered \"%s\"\n", g_variant_get_boolean(value) ? "on" : "off");
}
if(!g_strcmp0(key, "Discovering")) {
if(!g_variant_is_of_type(value, G_VARIANT_TYPE_BOOLEAN)) {
g_print("Invalid argument type for %s: %s != %s", key,
g_variant_get_type_string(value), "b");
goto done;
}
g_print("Adapter scan \"%s\"\n", g_variant_get_boolean(value) ? "on" : "off");
}
}
done:
if(properties != NULL)
g_variant_iter_free(properties);
if(value != NULL)
g_variant_unref(value);
}
static int bluez_adapter_set_property(const char *prop, GVariant *value)
{
GVariant *result;
GError *error = NULL;
result = g_dbus_connection_call_sync(con,
"org.bluez",
"/org/bluez/hci0",
"org.freedesktop.DBus.Properties",
"Set",
g_variant_new("(ssv)", "org.bluez.Adapter1", prop, value),
NULL,
G_DBUS_CALL_FLAGS_NONE,
-1,
NULL,
&error);
if(error != NULL)
return 1;
g_variant_unref(result);
return 0;
}
static int bluez_adapter_connect_device(char **argv)
{
int rc;
GVariantBuilder *b = g_variant_builder_new(G_VARIANT_TYPE_VARDICT);
g_variant_builder_add(b, "{sv}", "Address", g_variant_new_string(argv[1]));
GVariant *device_dict = g_variant_builder_end(b);
g_variant_builder_unref(b);
rc = bluez_adapter_call_method("ConnectDevice",
g_variant_new_tuple(&device_dict, 1),
bluez_result_async_cb);
if(rc) {
g_print("Not able to call ConnectDevice\n");
return 1;
}
return 0;
}
#define AGENT_PATH "/org/bluez/AutoPinAgent"
static int bluez_agent_call_method(const gchar *method, GVariant *param)
{
GVariant *result;
GError *error = NULL;
result = g_dbus_connection_call_sync(con,
"org.bluez",
"/org/bluez",
"org.bluez.AgentManager1",
method,
param,
NULL,
G_DBUS_CALL_FLAGS_NONE,
-1,
NULL,
&error);
if(error != NULL) {
g_print("Register %s: %s\n", AGENT_PATH, error->message);
return 1;
}
g_variant_unref(result);
return 0;
}
static int bluez_register_autopair_agent(void)
{
int rc;
rc = bluez_agent_call_method("RegisterAgent", g_variant_new("(os)", AGENT_PATH, "NoInputNoOutput"));
if(rc)
return 1;
rc = bluez_agent_call_method("RequestDefaultAgent", g_variant_new("(o)", AGENT_PATH));
if(rc) {
bluez_agent_call_method("UnregisterAgent", g_variant_new("(o)", AGENT_PATH));
return 1;
}
return 0;
}
static void cleanup_handler(int signo)
{
if (signo == SIGINT) {
g_print("received SIGINT\n");
g_main_loop_quit(loop);
}
}
int main(int argc, char **argv)
{
int rc;
guint prop_changed;
guint iface_added;
guint iface_removed;
if(signal(SIGINT, cleanup_handler) == SIG_ERR)
g_print("can't catch SIGINT\n");
con = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, NULL);
if(con == NULL) {
g_print("Not able to get connection to system bus\n");
return 1;
}
loop = g_main_loop_new(NULL, FALSE);
prop_changed = g_dbus_connection_signal_subscribe(con,
"org.bluez",
"org.freedesktop.DBus.Properties",
"PropertiesChanged",
NULL,
"org.bluez.Adapter1",
G_DBUS_SIGNAL_FLAGS_NONE,
bluez_signal_adapter_changed,
NULL,
NULL);
iface_added = g_dbus_connection_signal_subscribe(con,
"org.bluez",
"org.freedesktop.DBus.ObjectManager",
"InterfacesAdded",
NULL,
NULL,
G_DBUS_SIGNAL_FLAGS_NONE,
bluez_device_appeared,
loop,
NULL);
iface_removed = g_dbus_connection_signal_subscribe(con,
"org.bluez",
"org.freedesktop.DBus.ObjectManager",
"InterfacesRemoved",
NULL,
NULL,
G_DBUS_SIGNAL_FLAGS_NONE,
bluez_device_disappeared,
loop,
NULL);
rc = bluez_adapter_set_property("Powered", g_variant_new("b", TRUE));
if(rc) {
g_print("Not able to enable the adapter\n");
goto fail;
}
rc = bluez_register_autopair_agent();
if(rc) {
g_print("Not able to register default autopair agent\n");
goto fail;
}
if(argc == 2) {
rc = bluez_adapter_connect_device(argv);
if(rc)
goto fail;
}
g_main_loop_run(loop);
rc = bluez_adapter_set_property("Powered", g_variant_new("b", FALSE));
if(rc)
g_print("Not able to disable the adapter\n");
fail:
g_dbus_connection_signal_unsubscribe(con, prop_changed);
g_dbus_connection_signal_unsubscribe(con, iface_added);
g_dbus_connection_signal_unsubscribe(con, iface_removed);
g_object_unref(con);
return 0;
}
you should be able to use the above program to connect the device. Here in this example, the agent is registered as "NoInputOutput" capability, something like bluetooth headphones, so that no pairing response is required.
But you should modify this example to client side (assuming this example going to run in RPi 1 as server, modify this to accept request in client side RPi 2).
You can find the detailed explanation about this example here and also some relevant GDBUS based examples here.
My Driver Code :
static irqreturn_t pwm_cnt_interrupt(int irq, void *data)
{
printk("==> %s\r\n", __func__);
return IRQ_HANDLED;
}
static int ecap_cnt_probe(struct platform_device *pdev)
{
pc = devm_kzalloc(&pdev->dev, sizeof(*pc), GFP_KERNEL);
if (!pc)
return -ENOMEM;
clk = devm_clk_get(&pdev->dev, "fck");
if (IS_ERR(clk)) {
if (of_device_is_compatible(np, "ti,counter-ecap")) {
dev_warn(&pdev->dev, "Binding is obsolete.\n");
clk = devm_clk_get(pdev->dev.parent, "fck");
}
}
if (IS_ERR(clk)) {
dev_err(&pdev->dev, "failed to get clock\n");
return PTR_ERR(clk);
}
pc->clk_rate = clk_get_rate(clk);
if (!pc->clk_rate) {
dev_err(&pdev->dev, "failed to get clock rate\n");
return -EINVAL;
}
/* Get PWM IRQ number */
ecapirq = platform_get_irq(pdev, 0);
if (ecapirq < 0) {
printk(KERN_ERR "Could not get IRQ");
return -EINVAL;
}
printk(KERN_DEBUG "irq = %d\n", ecapirq);
oreore_dentry = debugfs_create_file("counter", 0666, NULL, &value, &fops);
if(request_irq(ecapirq, pwm_cnt_interrupt, IRQF_SHARED,
"counter", (void *)&counter)) {
printk(KERN_ERR "pwm counter: Can't allocate irq %d\n",
ecapirq);
return -EBUSY;
}
enable_irq(ecapirq);
return 0;
}
My Interrupt got registed in /proc/interrupts
But, Its not get triggered.
I have connected UART with my pwm interrupt pin. I m sending data using uart port. my irq handler is not get called at that time.
Need help on this.
In a namespace extension, I'm creating a thread and passing in a file path to the thread function.
The problem I'm seeing is the first character of the file path gets corrupted. D:\temp0.csv gets passed in and in the thread function it becomes Y:\temp0.csv or some other random corrupted first wchar. In Win2k8R2 and Win10 it was working fine, but sometimes it would fail in the same way. I tried disabling optimizations to no avail.
The fileName variable is populated from the IShellItem that came from the IFileOpenDialog.
What do I need to do to fix this?
Caller of thread function:
LPWSTR filePath = NULL;
IFileOpenDialog *ofd = NULL;
IShellItem *file = NULL;
hrPath = file->GetDisplayName(SIGDN_FILESYSPATH, &filePath);
CreateThread(NULL, 0, CCsv::BuildTree, static_cast<LPVOID>(filePath), 0, NULL);
static class thread function
DWORD WINAPI CCsv::BuildTree(LPVOID lpParam) {
CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
LPWSTR filePath = static_cast<LPWSTR>(lpParam);
}
Here is a minimal program, but it does not repro with this code. One difference is I added a wait for the thread function. THat doesn't exist in the namespace extension.
main.cpp
// buf.cpp : Defines the entry point for the console application.
//
#pragma once
#include "stdafx.h"
using std::wstring;
static CRITICAL_SECTION g_TreeLock;
static CRITICAL_SECTION g_MountQueueLock;
class CCsv
{
public:
CCsv();
~CCsv();
static DWORD WINAPI BuildTree(LPVOID lpParam);
};
class CMountPath {
public:
CMountPath();
~CMountPath();
BOOL Mount();
BOOL PathExists(LPWSTR path);
private:
CSimpleArray<wstring> m_MountQueue;
};
extern CCsv g_Csv;
CCsv::CCsv() {
InitializeCriticalSection(&g_TreeLock);
}
CCsv::~CCsv() {
DeleteCriticalSection(&g_TreeLock);
}
DWORD WINAPI CCsv::BuildTree(LPVOID lpParam) {
LPWSTR name = static_cast<LPWSTR>(lpParam);
MessageBox(NULL, name, L"", MB_OK);
CoTaskMemFree(name);
return 0;
}
CMountPath::CMountPath() {
InitializeCriticalSection(&g_MountQueueLock);
}
CMountPath::~CMountPath() {
DeleteCriticalSection(&g_MountQueueLock);
}
BOOL CMountPath::PathExists(LPWSTR path) {
return FALSE;
}
BOOL CMountPath::Mount() {
IEnumIDList *idl = NULL;
LPITEMIDLIST pidl = NULL;
LPITEMIDLIST desktopPidl = NULL;
LPCITEMIDLIST pidlRelative = NULL;
BOOL success = FALSE;
HRESULT hr, hrPath = S_FALSE;
LPWSTR filePath = NULL;
PWSTR filePathHeap = NULL;
WCHAR msg[MAXPATH+MAXMSG] = {0};
IFileOpenDialog *ofd = NULL;
IShellItem *file = NULL;
DWORD idx = 0;
BOOL isQueued = FALSE;
const COMDLG_FILTERSPEC fileSpec[] = {
{ L"CSV Text Files", L"*.csv" },
{ L"All Files", L"*.*" },
};
hr = CoCreateInstance(CLSID_FileOpenDialog, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&ofd));
if (SUCCEEDED(hr)) {
if (SUCCEEDED(hr)){
ofd->SetTitle(L"Choose file");
ofd->SetFileTypes(ARRAYSIZE(fileSpec), fileSpec);
hr = ofd->Show(NULL);
if(SUCCEEDED(hr))
hr = ofd->GetResult(&file);
if(SUCCEEDED(hr))
hrPath = file->GetDisplayName(SIGDN_FILESYSPATH, &filePath);
if(SUCCEEDED(hrPath)){
LPWSTR filePathHeap = (LPWSTR)CoTaskMemAlloc(MAXPATH * sizeof(WCHAR));
if(filePathHeap) {
StringCchCopy(filePathHeap, MAXPATH, filePath);
if(PathExists(filePathHeap)) {
StringCchPrintf(msg, MAXPATH+MAXMSG, L"The file %s is already loaded.", filePathHeap);
MessageBox(NULL, msg, L"appname", MB_OK);
}
else {
EnterCriticalSection(&g_MountQueueLock);
isQueued = !m_MountQueue.Find(wstring(filePathHeap)) ? TRUE : FALSE;
if(!isQueued)
m_MountQueue.Add(wstring(filePathHeap));
LeaveCriticalSection(&g_MountQueueLock);
if(!isQueued) {
HANDLE hThread = CreateThread(NULL, 0, CCsv::BuildTree, static_cast<LPVOID>(filePathHeap), 0, NULL);
// there is no wait in the namespace extension. the wait is just to keep the console app main thread running.
if(INVALID_HANDLE_VALUE != hThread)
WaitForSingleObject(hThread, INFINITE);
}
else {
StringCchPrintf(msg, MAXPATH+MAXMSG, L"The file %s is already being loaded.", filePathHeap);
MessageBox(NULL, msg, L"appname", MB_OK);
}
}
}
CoTaskMemFree(filePath);
file->Release();
}
}
ofd->Release();
}
return success;
}
int main() {
CoInitialize(NULL);
CMountPath m;
m.Mount();
CoUninitialize();
return 0;
}
stdafx.h
#pragma once
#define MAXPATH 32767
#define MAXMSG 128
#define WIN32_LEAN_AND_MEAN
#define WINVER 0x0600
#define _WIN32_WINNT 0x0600
#include "targetver.h"
#include <stdio.h>
#include <tchar.h>
#include <windows.h>
#include <atlbase.h>
#include <atlstr.h>
#include <atlcoll.h>
#include <shlobj.h>
#include <Shobjidl.h>
#include <ShlGuid.h>
#include <shellapi.h>
#include <OleAuto.h>
#include <shlwapi.h>
#include <strsafe.h>
#include <string>
Why are you using threads at all? When you spawn a new thread, you are blocking your code waiting for the thread to terminate before continuing, so you are serializing all of your code. You may as well not even use threads at all.
Also, you have memory leaks and various logic errors in your code.
Try this instead:
// buf.cpp : Defines the entry point for the console application.
//
#pragma once
#include "stdafx.h"
using std::wstring;
class CCsv
{
public:
CCsv();
~CCsv();
void BuildTree(LPCWSTR name);
private:
CRITICAL_SECTION m_TreeLock;
};
class CMountPath {
public:
CMountPath();
~CMountPath();
BOOL Mount();
BOOL PathExists(LPCWSTR path);
private:
CSimpleArray<wstring> m_MountQueue;
CRITICAL_SECTION m_MountQueueLock;
};
CCsv g_Csv;
CCsv::CCsv() {
InitializeCriticalSection(&m_TreeLock);
}
CCsv::~CCsv() {
DeleteCriticalSection(&m_TreeLock);
}
void CCsv::BuildTree(LPCWSTR name) {
MessageBoxW(NULL, name, L"", MB_OK);
}
CMountPath::CMountPath() {
InitializeCriticalSection(&m_MountQueueLock);
}
CMountPath::~CMountPath() {
DeleteCriticalSection(&m_MountQueueLock);
}
BOOL CMountPath::PathExists(LPCWSTR path) {
return FALSE;
}
BOOL CMountPath::Mount() {
BOOL success = FALSE;
HRESULT hr = S_FALSE;
LPWSTR filePath = NULL;
WCHAR msg[MAXPATH+MAXMSG] = {0};
IFileOpenDialog *ofd = NULL;
IShellItem *file = NULL;
BOOL isQueued = FALSE;
const COMDLG_FILTERSPEC fileSpec[] = {
{ L"CSV Text Files", L"*.csv" },
{ L"All Files", L"*.*" },
};
hr = CoCreateInstance(CLSID_FileOpenDialog, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&ofd));
if (SUCCEEDED(hr)) {
ofd->SetTitle(L"Choose file");
ofd->SetFileTypes(ARRAYSIZE(fileSpec), fileSpec);
hr = ofd->Show(NULL);
if(SUCCEEDED(hr))
hr = ofd->GetResult(&file);
if(SUCCEEDED(hr)) {
hr = file->GetDisplayName(SIGDN_FILESYSPATH, &filePath);
if(SUCCEEDED(hr)){
if(PathExists(filePath)) {
StringCchPrintf(msg, ARRAYSIZE(msg), L"The file %s is already loaded.", filePath);
MessageBox(NULL, msg, L"appname", MB_OK);
}
else {
EnterCriticalSection(&m_MountQueueLock);
isQueued = !m_MountQueue.Find(filePath) ? TRUE : FALSE;
if(!isQueued)
m_MountQueue.Add(filePath);
LeaveCriticalSection(&m_MountQueueLock);
if(!isQueued) {
CCsv::BuildTree(filePath);
}
else {
StringCchPrintf(msg, ARRAYSIZE(msg), L"The file %s is already being loaded.", filePath);
MessageBox(NULL, msg, L"appname", MB_OK);
}
}
CoTaskMemFree(filePath);
}
file->Release();
}
ofd->Release();
}
return success;
}
int main() {
CoInitialize(NULL);
CMountPath m;
m.Mount();
CoUninitialize();
return 0;
}
your conceptual mistake here:
if(!isQueued)
{
m_MountQueue.Add(wstring(filePathHeap));
CreateThread(NULL, 0, CCsv::BuildTree, static_cast<LPVOID>(filePathHeap), 0, NULL);
}
so you insert filePathHeap in some database and simultaneously pass this filePathHeap to some thread. question - who is OWNER of filePathHeap ? who must free it ? if you free it from BuildTree - in m_MountQueue will be invalid pointer ? you not show code who and how handle m_MountQueue - may be this code pop and free filePathHeap before it used in BuildTree.
in general - if you push filePathHeap to m_MountQueue - you must not direct use it pointer after this, but have working thread (pool) which pop data from m_MountQueue , process and free it. or if you direct use filePathHeap you must not insert it to m_MountQueue.
in case if you need simultaneously working with filePathHeap from several threads - you need ref count have. some like this:
class CFilePath
{
PWSTR _filePath;
LONG _dwRef;
~CFilePath()
{
CoTaskMemFree(_filePath);
}
public:
PCWSTR getPath() { return _filePath; }
CFilePath(PWSTR filePath)
{
_filePath = filePath;
_dwRef = 1;
}
void AddRef()
{
InterlockedIncrement(&_dwRef);
}
void Release()
{
if (!InterlockedDecrement(&_dwRef)) delete this;
}
};
ULONG CALLBACK CCsv::BuildTree(CFilePath* p)
{
MessageBoxW(0, p->getPath(), L"use path in thread 2", MB_OK);
p->Release();
return 0;
}
BOOL CMountPath::Mount() {
...
if (CFilePath* p = new CFilePath(filePath))
{
p->AddRef();
if (HANDLE hThread = CreateThread(0, 0, reinterpret_cast<PTHREAD_START_ROUTINE>(CCsv::BuildTree), p, 0, 0))
{
CloseHandle(hThread);
}
else
{
p->Release();
}
MessageBoxW(0, p->getPath(), L"use path in thread 1", MB_OK);
p->Release();
}
else
{
CoTaskMemFree(filePath);
}
...
}
Secondly - this code
LPWSTR filePathHeap = (LPWSTR)CoTaskMemAlloc(MAXPATH * sizeof(WCHAR));
if(filePathHeap) StringCchCopy(filePathHeap, MAXPATH, filePath);
absolutely unnecessary. you can direct use filePath as is, but not reallocate it. for what ?!
thirdly - as in previous answer - almost no sense create thread and after this just wait on it exit.
HANDLE hThread = CreateThread(NULL, 0, CCsv::BuildTree, static_cast<LPVOID>(filePathHeap), 0, NULL);
// there is no wait in the namespace extension. the wait is just to keep the console app main thread running.
if(INVALID_HANDLE_VALUE != hThread)
WaitForSingleObject(hThread, INFINITE);
why in this case not direct call CCsv::BuildTree(filePathHeap); with same effect ?
also can note that INVALID_HANDLE_VALUE returned only for CreateFile or Socket - so for file handles only. all another object creating api (including CreateThread return 0 on error, but not INVALID_HANDLE_VALUE (-1) ) - so you incorrect check error condition.
and finally who will call CloseHandle(hThread) ? handle not auto closed when thread exit
I am trying to create a JNI wrapper for this windows application called 'Personal Communications' through a COM Object that it supplies. PCOM Help
Through lots of googling, I have managed alot on my own. I can suffessfully connect to the COM Object and run getter/setter methods to retrieve various info on the application.
My problem is now with Sink Objects (trying to get the application to send me events).
As far as I can tell, everything is returning a good return code, but the Invoke on the Sink Class is never called. Whats worse, if a Event was suppose to be called, Releasing the COM object that told it to start sending events, hangs the application. If I end the application advising it of the sink but not making it fire any events, the application doesn't hang.
I have tried various methods and compilers. They all give the same result. Whats weird though, if I use the same code in an actual application (exe) outside of Java, everything works fine and the Events are fired through the Sink Object. So I am truly at a lost for whats wrong with Java in the picture.
Here is my code I have so far:
PcommControl.h (My Wrapper for controlling the COMs)
initConnectionManagerEvents: creates a new sink object and advises the COM of it. RegisterStartEvent tells the COM to start sending messages to the sink objects.
removeConnectionManagerEvents: unadvises and removes all sink objects. UnregisterStartEvent tells the COM to stop sending messages to the sink objects.
jobject PcommControl::initConnectionManagerEvents(jobject sinkType) {
if (!initConnectionManager())
return NULL;
if (autConnectionManagerPoint == NULL) {
autConnectionManagerPoint = getConnectionPoint(autConnectionManager,
DIID_IStartEvent);
if (autConnectionManagerPoint == NULL)
return NULL;
}
ConnectionSink *conSinkC = new ConnectionSink(jvm, sinkType);
if (!conSinkC->isInitialized()) {
delete conSinkC;
return NULL;
}
if (!conSinkC->Advise(autConnectionManagerPoint)) {
#ifdef DEBUG
printf("PcommControl: failed to advise ConnectionManagerEvent\n");
#endif
delete conSinkC;
return NULL;
}
#ifdef DEBUG
printf("PcommControl: ConnectionManagerEvent> %ld\n", conSinkC->getCookie());
#endif
ConnectionSink *temp = connectionManagerEvents.put(
(long) conSinkC->getCookie(), conSinkC);
if (temp) {
temp->Release();
}
if (connectionManagerEvents.getSize() == 1) {
#ifdef DEBUG
printf("PcommControl: Registering ConnectionManagerEvent\n");
#endif
HRESULT hresult = autConnectionManager->RegisterStartEvent();
if (!SUCCEEDED(hresult)) {
#ifdef DEBUG
printf("Failed to get RegisterStartEvent\n");
#endif
// TODO
}
}
return conSinkC->getJavaObjectConnection();
}
void PcommControl::removeConnectionManagerEvents() {
ConnectionSink *connectionSink;
if ((autConnectionManager) && (!safeUnload)) {
#ifdef DEBUG
printf("PcommControl: Unregistering ConnectionManagerEvent\n");
#endif
// TODO: seems to cause hanging issues for Java
HRESULT hresult = autConnectionManager->UnregisterStartEvent();
if (!SUCCEEDED(hresult)) {
#ifdef DEBUG
printf("Failed to UnregisterStartEvent\n");
#endif
}
}
while ((connectionSink = connectionManagerEvents.removeHead()) != NULL) {
if (!safeUnload) {
#ifdef DEBUG
printf("PcommControl: releasing a connection manager event\n");
#endif
connectionSink->Unadvise();
#ifdef DEBUG
printf("PcommControl: start release\n");
#endif
connectionSink->Release();
}
}
#ifdef DEBUG
printf("PcommControl: done releasing ConnectionManager events\n");
#endif
}
JNIEventSink.h (My Sink Interface for all JNI Sinks)
#ifndef JNIEVENTSINK_H_
#define JNIEVENTSINK_H_
#include <jni.h>
#include <OCIdl.h>
#define FARFAR FAR* FAR*
class JNIEventSink: public IDispatch {
protected:
private:
bool initialized;
bool deconstructor;
DWORD referenceCount;
JavaVM *jvm;
jobject javaObjectConnection;
DWORD cookie;
IConnectionPoint *point;
static jclass javaLangClass;
static jmethodID javaLangClassNewInstance;
void init(JavaVM *javaVM, jobject sinkType) {
initialized = false;
deconstructor = false;
referenceCount = 0;
jvm = javaVM;
javaObjectConnection = NULL;
cookie = 0;
AddRef();
// create Java sink class from sinkType
// if (javaVM) {
// JNIEnv *env;
// javaVM->AttachCurrentThread((void **) &env, NULL);
// if (env == NULL) {
//#ifdef DEBUG
// printf("JNIEventSink: java environment not found!\n");
//#endif
// return;
// }
//
// if (javaLangClass == NULL) {
// javaLangClass = NULL;
// javaLangClassNewInstance = NULL;
//
// javaLangClass = env->FindClass("java/lang/Class");
// if (javaLangClass == NULL) {
//#ifdef DEBUG
// printf("JNIEventSink: javaLangClass not found!\n");
//#endif
// return;
// }
// javaLangClassNewInstance = env->GetMethodID(javaLangClass,
// "newInstance", "()Ljava/lang/Object;");
// if (javaLangClassNewInstance == NULL) {
//#ifdef DEBUG
// printf(
// "JNIEventSink: javaLangClass NewInstance not found!\n");
//#endif
// return;
// }
// }
//
// javaObjectConnection = env->CallObjectMethod(sinkType,
// javaLangClassNewInstance);
// if (javaObjectConnection == NULL) {
//#ifdef DEBUG
// printf(
// "JNIEventSink: Failed to create new Connection Object!\n");
//#endif
// return;
// }
// }
initialized = true;
}
public:
bool test;
JNIEventSink(JavaVM *javaVM, jobject sinkType) {
#ifdef DEBUG
printf("JNIEventSink: constructor\n");
#endif
init(javaVM, sinkType);
test = false;
}
virtual ~JNIEventSink() {
#ifdef DEBUG
printf("JNIEventSink: deconstructor\n");
if (test)
printf("YESYESYESYESYESYESYESYES\n");
#endif
deconstructor = true;
//
// if (point != NULL)
// Unadvise();
//
// if (referenceCount > 0)
// Release();
}
bool isInitialized() {
return initialized;
}
bool Advise(IConnectionPoint *point) {
#ifdef DEBUG
printf("JNIEventSink: Start Advise\n");
#endif
this->point = point;
this->point->AddRef();
HRESULT hresult = point->Advise(this, &cookie);
// TODO set cookie to java class
#ifdef DEBUG
printf("JNIEventSink: Advise End\n");
if (!SUCCEEDED(hresult))
printf("JNIEventSink: failed\n");
#endif
return SUCCEEDED(hresult);
}
bool Unadvise() {
#ifdef DEBUG
printf("JNIEventSink: Start Unadvise\n");
#endif
if (point == NULL)
return true;
IConnectionPoint *point = this->point;
this->point = NULL;
HRESULT hresult = point->Unadvise(cookie);
point->Release();
#ifdef DEBUG
printf("JNIEventSink: Unadvise End\n");
if (!SUCCEEDED(hresult))
printf("JNIEventSink: failed\n");
#endif
return SUCCEEDED(hresult);
}
DWORD getCookie() {
return cookie;
}
jobject getJavaObjectConnection() {
return javaObjectConnection;
}
ULONG
STDMETHODCALLTYPE AddRef() {
#ifdef DEBUG
printf("JNIEventSink: Add Ref %ld,%ld,%ld\n", (long) this, cookie,
referenceCount);
#endif
referenceCount++;
return referenceCount;
}
ULONG
STDMETHODCALLTYPE Release() {
#ifdef DEBUG
printf("JNIEventSink: Start Release %ld,%ld,%ld\n", (long) this,
cookie, referenceCount);
#endif
if (referenceCount == 0) {
#ifdef DEBUG
printf("0 ref\n");
#endif
return 0;
}
referenceCount--;
long temp = referenceCount;
if ((temp == 0) && (!deconstructor)) {
#ifdef DEBUG
printf("JNIEventSink: deleting\n");
#endif
delete this;
}
return temp;
}
virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid,
void **ppvObject) {
#ifdef DEBUG
printf("JNIEventSink: QueryInterface %ld,%ld\n", (long) this, cookie);
#endif
if (iid == IID_IUnknown) {
*ppvObject = (IUnknown *) this;
} else if (iid == IID_IDispatch) {
*ppvObject = (IDispatch *) this;
} else {
*ppvObject = (void *) this;
}
((IUnknown *) (*ppvObject))->AddRef();
return S_OK;
}
virtual HRESULT STDMETHODCALLTYPE GetTypeInfoCount(UINT *pctinfo) {
#ifdef DEBUG
printf("JNIEventSink: GetTypeInfoCount %ld,%ld\n", (long) this, cookie);
#endif
return E_NOTIMPL;
}
virtual HRESULT STDMETHODCALLTYPE GetIDsOfNames(REFIID riid,
LPOLESTR *rgszNames, UINT cNames, LCID lcid, DISPID *rgDispId) {
#ifdef DEBUG
printf("JNIEventSink: GetIDsOfNames %ld,%ld\n", (long) this, cookie);
#endif
return E_NOTIMPL;
}
virtual HRESULT STDMETHODCALLTYPE GetTypeInfo(unsigned int iTInfo,
LCID lcid, ITypeInfo FARFAR ppTInfo) {
#ifdef DEBUG
printf("JNIEventSink: GetTypeInfo %ld,%ld\n", (long) this, cookie);
#endif
return E_NOTIMPL;
}
// virtual HRESULT STDMETHODCALLTYPE Invoke(DISPID dispIdMember, REFIID riid,
// LCID lcid, WORD wFlags, DISPPARAMS FAR* pDispParams,
// VARIANT FAR* pVarResult, EXCEPINFO FAR* pExcepInfo,
// unsigned int FAR* puArgErr) = 0;
};
jclass JNIEventSink::javaLangClass = NULL;
jmethodID JNIEventSink::javaLangClassNewInstance = NULL;
#endif /* JNIEVENTSINK_H_ */
ConnectionSink.h (My Implementation of the JNI Sink that is suppose to get all Events from autConnectionManager)
#ifndef CONNECTIONSINK_H_
#define CONNECTIONSINK_H_
#include <jni.h>
#include "PcommInterfaces.h"
#include "..\COM\JNIEventSink.h"
class ConnectionSink: public JNIEventSink {
private:
public:
ConnectionSink(JavaVM *javaVM, jobject sinkType) :
JNIEventSink(javaVM, sinkType) {
}
virtual ~ConnectionSink() {
#ifdef DEBUG
printf("ConnectionSink: deconstructor\n");
#endif
}
// IStartEvent
// future events I want to call
// 1 - void NotifyStartEvent(VARIANT ConnHandle, VARIANT_BOOL bStarted);
// 2 - void NotifyStartError(VARIANT ConnHandle);
// 3 - void NotifyStartStop(int* Reason);
virtual HRESULT STDMETHODCALLTYPE Invoke(DISPID dispIdMember, REFIID riid,
LCID lcid, WORD wFlags, DISPPARAMS FAR* pDispParams,
VARIANT FAR* pVarResult, EXCEPINFO FAR* pExcepInfo,
unsigned int FAR* puArgErr) {
// TODO this seems like it is never called
#ifdef DEBUG
printf("ConnectionSink: Invoke %ld,%ld,%ld\n", (long) this,
this->getCookie(), dispIdMember);
#endif
test = true;
return S_OK;
//return E_NOTIMPL;
}
};
#endif /* CONNECTIONSINK_H_ */
Example output when it hangs:
Java_gov_ssa_utils_pcomm_ConnectionManager_registerStartEvents
JNIEventSink: constructor
JNIEventSink: Add Ref 72880304,0,0
JNIEventSink: Start Advise
JNIEventSink: Add Ref 72880304,0,1
JNIEventSink: Advise End
PcommControl: ConnectionManagerEvent> 1
PcommControl: Registering ConnectionManagerEvent
Created and attached sink.
Waiting 10 seconds for user to fire event
Destroying.
Java_gov_ssa_utils_PComm_release
PcommControl: destroying
PcommControl: Unregistering ConnectionManagerEvent
Thanks for any help.
I figured out my problem.
The thread that created the sink object needs to also pump the thread's message queue using the window's function 'GetMessage'.
This is why the windows executable was working correctly, it already have a message pump for the window that was created. I figured this out when I put a breakpoint in the sink object when the invoke was being called and looked at the stack trace. 'GetMessage' was the first thing on the trace that came from my code.
In this instance, I believe why 'GetMessage' is the answer is because I believe the COM's code is calling 'PostThreadMessage' to notify me that a sink event has occurred and 'GetMessage' looks at these messages and somehow knows they are COM related and handle them on its own.
PS: Notice I said the thread that created the sink. If you create the sink in one thread and have the message pump in another, you will receive the message from the COM about the sink, but it won't know how to handle it automatically since this thread has nothing loaded about the COM in its space. So the sink object's invoke is never called.
So Java could access the same COM everywhere, I made the 2nd thread control everything about the COM, and any native methods that were called redirect the request to the that thread. So no matter what thread Java is running in, it will always have access to the same COM without having to reload everything.