Example #1
0
/* FIXME: share w/ libutil is_wow64() */
bool
is_wow64(HANDLE hProcess)
{
    /* IsWow64Pocess is only available on XP+ */
    typedef DWORD (WINAPI *IsWow64Process_Type)(HANDLE hProcess,
                                                PBOOL isWow64Process);
    static HANDLE kernel32_handle;
    static IsWow64Process_Type IsWow64Process;
    if (kernel32_handle == NULL)
        kernel32_handle = GetModuleHandle("kernel32.dll");
    if (IsWow64Process == NULL && kernel32_handle != NULL) {
        IsWow64Process = (IsWow64Process_Type)
            GetProcAddress(kernel32_handle, "IsWow64Process");
    }
    if (IsWow64Process == NULL) {
        /* should be NT or 2K */
        assert(get_windows_version() == WINDOWS_VERSION_NT ||
               get_windows_version() == WINDOWS_VERSION_2000);
        return false;
    } else {
        BOOL res;
        if (!IsWow64Process(hProcess, &res))
            return false;
        return CAST_TO_bool(res);
    }
}
Example #2
0
void
syscall_wingdi_init(void *drcontext, app_pc ntdll_base)
{
    get_sysnum("NtGdiCreateDIBSection", &sysnum_GdiCreateDIBSection,
               get_windows_version() <= DR_WINDOWS_VERSION_2000);

    get_sysnum("NtUserGetDC", &sysnum_UserGetDC, false/*reqd*/);
    get_sysnum("NtUserGetDCEx", &sysnum_UserGetDCEx, false/*reqd*/);
    get_sysnum("NtUserGetWindowDC", &sysnum_UserGetWindowDC, false/*reqd*/);
    get_sysnum("NtUserBeginPaint", &sysnum_UserBeginPaint, false/*reqd*/);
    get_sysnum("NtUserEndPaint", &sysnum_UserEndPaint, false/*reqd*/);
    get_sysnum("ReleaseDC", &sysnum_UserReleaseDC,
               get_windows_version() <= DR_WINDOWS_VERSION_2000);
    get_sysnum("NtGdiGetDCforBitmap", &sysnum_GdiGetDCforBitmap,
               get_windows_version() <= DR_WINDOWS_VERSION_2000);
    get_sysnum("NtGdiDdGetDC", &sysnum_GdiDdGetDC,
               get_windows_version() <= DR_WINDOWS_VERSION_2000);
    get_sysnum("NtGdiDeleteObjectApp", &sysnum_GdiDeleteObjectApp,
               get_windows_version() <= DR_WINDOWS_VERSION_2000);
    get_sysnum("NtGdiCreateMetafileDC", &sysnum_GdiCreateMetafileDC,
               get_windows_version() <= DR_WINDOWS_VERSION_2000);
    get_sysnum("NtGdiCreateCompatibleDC", &sysnum_GdiCreateCompatibleDC,
               get_windows_version() <= DR_WINDOWS_VERSION_2000);
    get_sysnum("NtGdiOpenDCW", &sysnum_GdiOpenDCW,
               get_windows_version() <= DR_WINDOWS_VERSION_2000);

    if (options.check_gdi)
        gdicheck_init();
}
Example #3
0
std::string
get_local_console_charset() {
#if defined(SYS_WINDOWS)
  if (get_windows_version() >= WINDOWS_VERSION_VISTA)
    return std::string("CP") + to_string(GetACP());
  return std::string("CP") + to_string(GetOEMCP());
#else
  return get_local_charset();
#endif
}
Example #4
0
void
mux_dialog::run() {
    auto &arg_list = static_cast<mmg_dialog *>(GetParent())->get_command_line_args();

    opt_file_name.Printf(wxT("%smmg-mkvmerge-options-%d-%d"), get_temp_dir().c_str(), (int)wxGetProcessId(), (int)wxGetUTCTime());
    try {
        const unsigned char utf8_bom[3] = {0xef, 0xbb, 0xbf};
        wxFile opt_file{opt_file_name, wxFile::write};
        opt_file.Write(utf8_bom, 3);

        for (size_t i = 1; i < arg_list.Count(); i++) {
            if (arg_list[i].IsEmpty())
                opt_file.Write(wxT("#EMPTY#"));
            else {
                auto arg_utf8 = escape(to_utf8(arg_list[i]));
                opt_file.Write(arg_utf8.c_str(), arg_utf8.length());
            }
            opt_file.Write(wxT("\n"));
        }
    } catch (mtx::mm_io::exception &ex) {
        wxString error;
        error.Printf(Z("Could not create a temporary file for mkvmerge's command line option called '%s' (error code %d, %s)."), opt_file_name.c_str(), errno, wxUCS(ex.error()));
        wxMessageBox(error, Z("File creation failed"), wxOK | wxCENTER | wxICON_ERROR);
        throw 0;
    }

#if defined(SYS_WINDOWS)
    if (get_windows_version() >= WINDOWS_VERSION_7) {
        m_taskbar_progress = new taskbar_progress_c(mdlg);
        m_taskbar_progress->set_state(TBPF_NORMAL);
        m_taskbar_progress->set_value(0, 100);
    }
#endif  // SYS_WINDOWS

    update_label(Z("Muxing in progress."));

    m_start_time                 = get_current_time_millis();
    m_next_remaining_time_update = m_start_time + 8000;

    m_process                    = new mux_process{this};
    m_pid                        = wxExecute(wxString::Format(wxT("\"%s\" \"@%s\""), arg_list[0].c_str(), opt_file_name.c_str()), wxEXEC_ASYNC, m_process);

    if (0 == m_pid) {
        wxCommandEvent evt(mux_process::event, mux_process::process_terminated);
        evt.SetInt(2);
        wxPostEvent(this, evt);

    } else {
        m_read_input_timer.SetOwner(this, ID_T_READ_INPUT);
        m_read_input_timer.Start(100);
    }

    ShowModal();
}
scanning_for_playlists_dlg::scanning_for_playlists_dlg(wxWindow *parent,
                                                       wxString const &original_file_name,
                                                       wxArrayString const &original_output,
                                                       std::vector<wxString> const &other_file_names)
  : wxDialog{parent, wxID_ANY, Z("Scanning directory")}
#if defined(SYS_WINDOWS)
  , m_taskbar_progress{}
#endif  // SYS_WINDOWS
  , m_scanner{new scan_directory_thread_c{this, other_file_names}}
  , m_aborted{}
  , m_progress{}
  , m_max_progress{other_file_names.size()}
  , m_start_time{}
  , m_next_remaining_time_update{}
{
	m_st_progress           = new wxStaticText(this, wxID_ANY,    wxU(boost::format(NY("%1% of %2% file processed", "%1% of %2% files processed", other_file_names.size())) % 0 % other_file_names.size()));
	m_g_progress            = new wxGauge(     this, wxID_ANY,    m_max_progress);
  auto st_remaining_label = new wxStaticText(this, wxID_ANY,    Z("Remaining time:"));
	m_st_remaining_time     = new wxStaticText(this, wxID_ANY,    Z("is being estimated"));
	m_b_abort               = new wxButton(    this, wxID_CANCEL, Z("&Abort"));

#if defined(SYS_WINDOWS)
  if (get_windows_version() >= WINDOWS_VERSION_7) {
    m_taskbar_progress = new taskbar_progress_c(mdlg);
    m_taskbar_progress->set_state(TBPF_NORMAL);
    m_taskbar_progress->set_value(0, other_file_names.size());
  }
#endif  // SYS_WINDOWS

  auto siz_remaining_time = new wxBoxSizer(wxHORIZONTAL);
  siz_remaining_time->Add(st_remaining_label,  0, wxALL, 5);
  siz_remaining_time->Add(m_st_remaining_time, 0, wxALL, 5);

	auto siz_button = new wxBoxSizer(wxHORIZONTAL);
	siz_button->AddStretchSpacer();
	siz_button->Add(m_b_abort, 0, wxALL, 5);
	siz_button->AddStretchSpacer();

	auto siz_all = new wxBoxSizer(wxVERTICAL);
	siz_all->Add(m_st_progress,      0, wxALL,            5);
	siz_all->Add(m_g_progress,       0, wxALL | wxEXPAND, 5);
	siz_all->Add(siz_remaining_time, 0, wxALL,            5);
	siz_all->Add(siz_button,         1, wxEXPAND,         5);

	SetSizerAndFit(siz_all);
  auto min_size = GetMinSize();
  SetSize(wxSize{min_size.GetWidth() + 50, min_size.GetHeight()});
	Layout();

	Centre(wxBOTH);

  parse_output(original_file_name, original_output);
}
Example #6
0
/** \brief Sets the priority mkvmerge runs with

   Depending on the OS different functions are used. On Unix like systems
   the process is being nice'd if priority is negative ( = less important).
   Only the super user can increase the priority, but you shouldn't do
   such work as root anyway.
   On Windows SetPriorityClass is used.

   \param priority A value between -2 (lowest priority) and 2 (highest
     priority)
 */
void
set_process_priority(int priority) {
#if defined(SYS_WINDOWS)
  static const struct {
    int priority_class, thread_priority;
  } s_priority_classes[5] = {
    { IDLE_PRIORITY_CLASS,         THREAD_PRIORITY_IDLE         },
    { BELOW_NORMAL_PRIORITY_CLASS, THREAD_PRIORITY_BELOW_NORMAL },
    { NORMAL_PRIORITY_CLASS,       THREAD_PRIORITY_NORMAL       },
    { ABOVE_NORMAL_PRIORITY_CLASS, THREAD_PRIORITY_ABOVE_NORMAL },
    { HIGH_PRIORITY_CLASS,         THREAD_PRIORITY_HIGHEST      },
  };

  // If the lowest priority should be used and we're on Vista or later
  // then use background priority. This also selects a lower I/O
  // priority.
  if ((-2 == priority) && (get_windows_version() >= WINDOWS_VERSION_VISTA)) {
    SetPriorityClass(GetCurrentProcess(), PROCESS_MODE_BACKGROUND_BEGIN);
    SetThreadPriority(GetCurrentThread(), THREAD_MODE_BACKGROUND_BEGIN);
    return;
  }

  SetPriorityClass(GetCurrentProcess(), s_priority_classes[priority + 2].priority_class);
  SetThreadPriority(GetCurrentThread(), s_priority_classes[priority + 2].thread_priority);

#else
  static const int s_nice_levels[5] = { 19, 2, 0, -2, -5 };

  // Avoid a compiler warning due to glibc having flagged 'nice' with
  // 'warn if return value is ignored'.
  if (!nice(s_nice_levels[priority + 2])) {
  }

# if defined(HAVE_SYSCALL) && defined(SYS_ioprio_set)
  if (-2 == priority)
    syscall(SYS_ioprio_set,
            1,        // IOPRIO_WHO_PROCESS
            0,        // current process/thread
            3 << 13); // I/O class 'idle'
# endif
#endif
}
Example #7
0
mux_dialog::mux_dialog(wxWindow *parent):
    wxDialog(parent, -1, Z("mkvmerge is running"), wxDefaultPosition,
#ifdef SYS_WINDOWS
             wxSize(700, 560),
#else
             wxSize(700, 520),
#endif
             wxDEFAULT_FRAME_STYLE)
#if defined(SYS_WINDOWS)
    , pid(0)
    , m_taskbar_progress(NULL)
    , m_abort_button_changed(false)
#endif  // SYS_WINDOWS
    , m_exit_code(0)
    , m_progress(0)
{
    char c;
    std::string arg_utf8, line;
    long value;
    wxString wx_line, tmp;
    wxInputStream *out;
    wxFile *opt_file;
    uint32_t i;
    wxArrayString *arg_list;
    wxBoxSizer *siz_all, *siz_buttons, *siz_line;
    wxStaticBoxSizer *siz_status, *siz_output;

    m_window_disabler = new wxWindowDisabler(this);

    c = 0;
    siz_status = new wxStaticBoxSizer(new wxStaticBox(this, -1, Z("Status and progress")), wxVERTICAL);
    st_label = new wxStaticText(this, -1, wxEmptyString);
    st_remaining_time_label = new wxStaticText(this, -1, Z("Remaining time:"));
    st_remaining_time       = new wxStaticText(this, -1, Z("is being estimated"));
    siz_line = new wxBoxSizer(wxHORIZONTAL);
    siz_line->Add(st_label);
    siz_line->AddSpacer(5);
    siz_line->Add(st_remaining_time_label);
    siz_line->AddSpacer(5);
    siz_line->Add(st_remaining_time);
    siz_status->Add(siz_line, 0, wxGROW | wxALIGN_LEFT | wxALL, 5);
    g_progress = new wxGauge(this, -1, 100, wxDefaultPosition, wxSize(250, 15));
    siz_status->Add(g_progress, 1, wxALL | wxGROW, 5);

    siz_output = new wxStaticBoxSizer(new wxStaticBox(this, -1, Z("Output")), wxVERTICAL);
    siz_output->Add(new wxStaticText(this, -1, Z("mkvmerge output:")), 0, wxALIGN_LEFT | wxALL, 5);
    tc_output = new wxTextCtrl(this, -1, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_READONLY | wxTE_BESTWRAP | wxTE_MULTILINE);
    siz_output->Add(tc_output, 2, wxGROW | wxALL, 5);
    siz_output->Add(new wxStaticText(this, -1, Z("Warnings:")), 0, wxALIGN_LEFT | wxALL, 5);
    tc_warnings = new wxTextCtrl(this, -1, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_READONLY | wxTE_BESTWRAP | wxTE_MULTILINE);
    siz_output->Add(tc_warnings, 1, wxGROW | wxALL, 5);
    siz_output->Add(new wxStaticText(this, -1, Z("Errors:")), 0, wxALIGN_LEFT | wxALL, 5);
    tc_errors = new wxTextCtrl(this, -1, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_READONLY | wxTE_BESTWRAP | wxTE_MULTILINE);
    siz_output->Add(tc_errors, 1, wxGROW | wxALL, 5);

    siz_buttons = new wxBoxSizer(wxHORIZONTAL);
    siz_buttons->AddStretchSpacer();
    b_ok = new wxButton(this, ID_B_MUX_OK, Z("Ok"));
    b_ok->Enable(false);
    siz_buttons->Add(b_ok, 0, wxGROW);
    siz_buttons->AddStretchSpacer();
    b_abort = new wxButton(this, ID_B_MUX_ABORT, Z("Abort"));
    siz_buttons->Add(b_abort, 0, wxGROW);
    siz_buttons->AddStretchSpacer();
    b_save_log = new wxButton(this, ID_B_MUX_SAVELOG, Z("Save log"));
    siz_buttons->Add(b_save_log, 0, wxGROW);
    siz_buttons->AddStretchSpacer();

    siz_all = new wxBoxSizer(wxVERTICAL);
    siz_all->Add(siz_status, 0, wxGROW | wxALL, 5);
    siz_all->Add(siz_output, 1, wxGROW | wxALL, 5);
    siz_all->Add(siz_buttons, 0, wxGROW | wxALL, 10);
    SetSizer(siz_all);

    update_window(Z("Muxing in progress."));
    Show(true);

    process = new mux_process(this);

    opt_file_name.Printf(wxT("%smmg-mkvmerge-options-%d-%d"), get_temp_dir().c_str(), (int)wxGetProcessId(), (int)wxGetUTCTime());
    try {
        const unsigned char utf8_bom[3] = {0xef, 0xbb, 0xbf};
        opt_file = new wxFile(opt_file_name, wxFile::write);
        opt_file->Write(utf8_bom, 3);
    } catch (...) {
        wxString error;
        error.Printf(Z("Could not create a temporary file for mkvmerge's command line option called '%s' (error code %d, %s)."), opt_file_name.c_str(), errno, wxUCS(strerror(errno)));
        wxMessageBox(error, Z("File creation failed"), wxOK | wxCENTER | wxICON_ERROR);
        throw 0;
    }
    arg_list = &static_cast<mmg_dialog *>(parent)->get_command_line_args();
    for (i = 1; i < arg_list->Count(); i++) {
        if ((*arg_list)[i].Length() == 0)
            opt_file->Write(wxT("#EMPTY#"));
        else {
            arg_utf8 = escape(wxMB((*arg_list)[i]));
            opt_file->Write(arg_utf8.c_str(), arg_utf8.length());
        }
        opt_file->Write(wxT("\n"));
    }
    delete opt_file;

#if defined(SYS_WINDOWS)
    if (get_windows_version() >= WINDOWS_VERSION_7) {
        m_taskbar_progress = new taskbar_progress_c(mdlg);
        m_taskbar_progress->set_state(TBPF_NORMAL);
        m_taskbar_progress->set_value(0, 100);
    }
#endif  // SYS_WINDOWS

    m_start_time                 = get_current_time_millis();
    m_next_remaining_time_update = m_start_time + 8000;

    wxString command_line = wxString::Format(wxT("\"%s\" \"@%s\""), (*arg_list)[0].c_str(), opt_file_name.c_str());
    pid = wxExecute(command_line, wxEXEC_ASYNC, process);
    if (0 == pid) {
        wxLogError(wxT("Execution of '%s' failed."), command_line.c_str());
        done(2);
        return;
    }
    out = process->GetInputStream();

    line = "";
    log = wxEmptyString;
    while (1) {
        while (app->Pending())
            app->Dispatch();

        if (!out->CanRead() && !out->Eof()) {
            wxMilliSleep(5);
            continue;
        }

        if (!out->Eof())
            c = out->GetC();
        else
            c = '\n';

        if ((c == '\n') || (c == '\r') || out->Eof()) {
            wx_line = wxU(line);
            log += wx_line;
            if (c != '\r')
                log += wxT("\n");
            if (wx_line.Find(Z("Warning:")) == 0)
                tc_warnings->AppendText(wx_line + wxT("\n"));
            else if (wx_line.Find(Z("Error:")) == 0)
                tc_errors->AppendText(wx_line + wxT("\n"));
            else if (wx_line.Find(Z("Progress")) == 0) {
                if (wx_line.Find(wxT("%")) != 0) {
                    wx_line.Remove(wx_line.Find(wxT("%")));
                    tmp = wx_line.AfterLast(wxT(' '));
                    tmp.ToLong(&value);
                    if ((value >= 0) && (value <= 100))
                        update_gauge(value);
                }
            } else if (wx_line.Length() > 0)
                tc_output->AppendText(wx_line + wxT("\n"));
            line = "";

            update_remaining_time();

        } else if ((unsigned char)c != 0xff)
            line += c;

        if (out->Eof())
            break;
    }
}
Example #8
0
/*  ---------------------------------------------------------------------[<]-
    Function: service_begin

    Synopsis: depending on arguments (from the command line):
      -i: install service               (windows)
      -u: remove  service               (windows)
      -d: runs service in console mode  (windows)
      -h: basic help information        (windows)
      -d: runs service in background    (UNIX / Linux)

      if no arguments, the service is actually started.

    NOTE: with Windows, the working directory is set to the one where the
    binary program stands.

    Returns: 0 is everything is OK, negative error code otherwise
    ---------------------------------------------------------------------[>]-*/
int
service_begin (
    int                   argc, 
    char                **argv,
    SMT_AGENTS_INIT_FCT  *init_fct, 
    SMT_AGENTS_TERM_FCT  *term_fct,
    const char           *appl_version)
{  
    int
        action;
    int
        rc = 0;

#if (defined(WIN32)) 
    static char
        buffer [LINE_MAX];
    char
        *p_char;

    SERVICE_TABLE_ENTRY 
        dispatch_table [] = {
        { NULL, (LPSERVICE_MAIN_FUNCTION) service_main },
        { NULL, NULL }
    };

    /*  Change to the correct working directory, where config file stands    */
    GetModuleFileName (NULL, buffer, LINE_MAX);
    if ((p_char = strrchr (buffer, '\\')) != NULL)
        *p_char = '\0';
    SetCurrentDirectory (buffer);
#endif

    rc = init_resources (argv[0], appl_version, init_fct, term_fct);
    if (rc != 0)
        return (1);

    ASSERT (application_config);        /* init_resources post condition     */
    if (load_service_config (application_config) != 0)
      {
        free_resources ();
        return (1);
      }

    ASSERT (service_trace_file);        /* load_service_config postcondition */
    console_set_mode (CONSOLE_DATETIME);
    console_capture (service_trace_file, 'a');

#if (defined(WIN32)) 
    dispatch_table [0].lpServiceName = service_name;
    win_version = get_windows_version ();
#endif

    action = parse_command_line (argc, argv);

    if (action == ACTION_HELP)
      {
        puts (USAGE);
      }
#if (defined(WIN32)) 
    else
    if (action == ACTION_INSTALL)
      {
        if (win_version == WINDOWS_95)
            set_win95_service (TRUE);
        else
            install_service ();
      }
    else 
    if (action == ACTION_UNINSTALL)
      {
        if (win_version == WINDOWS_95)
            set_win95_service (FALSE);
        else
            remove_service ();
      }
    else
    if (action == ACTION_CONSOLE)
      {
        console_mode  = TRUE;
        console_service (argc, argv);
      }
    else
    if (action == ACTION_NOARG)
      {
        console_send (NULL, FALSE);

        if (win_version == WINDOWS_95)
          {
            hide_window ();
            console_mode = TRUE;
            console_service (argc, argv);
          }
        else
        if (win_version == WINDOWS_NT_3X
        ||  win_version == WINDOWS_NT_4
        ||  win_version == WINDOWS_2000)
          {
            log_printf ("%s: initialising service ...", application_name);
            if (!StartServiceCtrlDispatcher (dispatch_table))
                add_to_message_log ("StartServiceCtrlDispatcher failed");
          }
      }
#elif (defined(__UNIX__))
    else
    if (action == ACTION_BACKGROUND)
      {
        const char
           *background_args [] = { "-s", NULL };

        log_printf ("Moving into the background");
        if (process_server (NULL, NULL, argc, argv, background_args) != 0)
          {
            log_printf ("Backgrounding failed.  Giving up.");
            rc = -1;
          }
        else
          action = ACTION_NOARG;
      }
    if (action == ACTION_NOARG)
      {
        rc = smt_init ();
        if (!rc && (init_fct != NULL))
            rc = (*init_fct)(application_config);
        if (!rc)
            smt_exec_full ();
        if (term_fct != NULL)
            (*term_fct)();
        smt_term ();
      }
#endif
    else
    if (action == ACTION_ERROR)
        puts (USAGE);
    
    free_resources ();
    return rc;
}
Example #9
0
static int windows_init(struct libusb_context *ctx)
{
	struct windows_context_priv *priv = _context_priv(ctx);
	HANDLE semaphore;
	char sem_name[11 + 8 + 1]; // strlen("libusb_init") + (32-bit hex PID) + '\0'
	int r = LIBUSB_ERROR_OTHER;
	bool winusb_backend_init = false;

	sprintf(sem_name, "libusb_init%08X", (unsigned int)(GetCurrentProcessId() & 0xFFFFFFFF));
	semaphore = CreateSemaphoreA(NULL, 1, 1, sem_name);
	if (semaphore == NULL) {
		usbi_err(ctx, "could not create semaphore: %s", windows_error_str(0));
		return LIBUSB_ERROR_NO_MEM;
	}

	// A successful wait brings our semaphore count to 0 (unsignaled)
	// => any concurent wait stalls until the semaphore's release
	if (WaitForSingleObject(semaphore, INFINITE) != WAIT_OBJECT_0) {
		usbi_err(ctx, "failure to access semaphore: %s", windows_error_str(0));
		CloseHandle(semaphore);
		return LIBUSB_ERROR_NO_MEM;
	}

	// NB: concurrent usage supposes that init calls are equally balanced with
	// exit calls. If init is called more than exit, we will not exit properly
	if (++init_count == 1) { // First init?
		// Load DLL imports
		if (!windows_init_dlls()) {
			usbi_err(ctx, "could not resolve DLL functions");
			goto init_exit;
		}

		get_windows_version();

		if (windows_version == WINDOWS_UNDEFINED) {
			usbi_err(ctx, "failed to detect Windows version");
			r = LIBUSB_ERROR_NOT_SUPPORTED;
			goto init_exit;
		}

		if (!windows_init_clock(ctx))
			goto init_exit;

		if (!htab_create(ctx))
			goto init_exit;

		r = winusb_backend.init(ctx);
		if (r != LIBUSB_SUCCESS)
			goto init_exit;
		winusb_backend_init = true;

		r = usbdk_backend.init(ctx);
		if (r == LIBUSB_SUCCESS) {
			usbi_dbg("UsbDk backend is available");
			usbdk_available = true;
		} else {
			usbi_info(ctx, "UsbDk backend is not available");
			// Do not report this as an error
			r = LIBUSB_SUCCESS;
		}
	}

	// By default, new contexts will use the WinUSB backend
	priv->backend = &winusb_backend;

	r = LIBUSB_SUCCESS;

init_exit: // Holds semaphore here
	if ((init_count == 1) && (r != LIBUSB_SUCCESS)) { // First init failed?
		if (winusb_backend_init)
			winusb_backend.exit(ctx);
		htab_destroy();
		windows_destroy_clock();
		windows_exit_dlls();
		--init_count;
	}

	ReleaseSemaphore(semaphore, 1, NULL); // increase count back to 1
	CloseHandle(semaphore);
	return r;
}