text:
using namespace eprosima::fastdds::rtps;
using namespace testing;

struct FlowControllerLimitedAsyncPublishModeMock : FlowControllerLimitedAsyncPublishMode
{
    FlowControllerLimitedAsyncPublishModeMock(
            eprosima::fastrtps::rtps::RTPSParticipantImpl* participant,
            const FlowControllerDescriptor* descriptor)
        : FlowControllerLimitedAsyncPublishMode(participant, descriptor)
    {
        group_mock = &group;
    }

    static eprosima::fastrtps::rtps::RTPSMessageGroup* get_group()
    {
        return group_mock;
    }

    static eprosima::fastrtps::rtps::RTPSMessageGroup* group_mock;
};
eprosima::fastrtps::rtps::RTPSMessageGroup* FlowControllerLimitedAsyncPublishModeMock::group_mock = nullptr;

TYPED_TEST(FlowControllerPublishModes, limited_async_publish_mode)
{
    FlowControllerDescriptor flow_controller_descr;
    flow_controller_descr.max_bytes_per_period = 10200;
    flow_controller_descr.period_ms = 10;
    FlowControllerImpl<FlowControllerLimitedAsyncPublishModeMock, TypeParam> async(nullptr,
            &flow_controller_descr);
    async.init();

    // Instantiate writers.
    eprosima::fastrtps::rtps::RTPSWriter writer1;
    eprosima::fastrtps::rtps::RTPSWriter writer2;

    // Initialize callback to get info.
    auto send_functor = [&](
        eprosima::fastrtps::rtps::CacheChange_t* change,
        eprosima::fastrtps::rtps::RTPSMessageGroup&,
        eprosima::fastrtps::rtps::LocatorSelectorSender&,
        const std::chrono::time_point<std::chrono::steady_clock>&)
            {
                this->last_thread_delivering_sample = std::this_thread::get_id();
                this->current_bytes_processed += change->serializedPayload.length;
                EXPECT_CALL(*FlowControllerLimitedAsyncPublishModeMock::get_group(),
                        get_current_bytes_processed()).WillRepeatedly(
                    ReturnPointee(&this->current_bytes_processed));
                {
                    std::unique_lock<std::mutex> lock(this->changes_delivered_mutex);
                    this->changes_delivered.push_back(change);
                }
                this->number_changes_delivered_cv.notify_one();
            };

    // Register writers.
    async.register_writer(&writer1);

    eprosima::fastrtps::rtps::CacheChange_t change_writer1;
    INIT_CACHE_CHANGE(change_writer1, writer1, 1);

    eprosima::fastrtps::rtps::CacheChange_t change_writer2;
    INIT_CACHE_CHANGE(change_writer2, writer2, 1);

    EXPECT_CALL(*FlowControllerLimitedAsyncPublishModeMock::get_group(),
            reset_current_bytes_processed()).WillRepeatedly([&]()
            {
                this->current_bytes_processed = 0;
            });

    // Testing add_new_sample. Writer will be able to deliver it.
    EXPECT_CALL(*FlowControllerLimitedAsyncPublishModeMock::get_group(), get_current_bytes_processed()).WillOnce(Return(
                0));
    EXPECT_CALL(writer1,
            deliver_sample_nts(&change_writer1, _, Ref(writer1.async_locator_selector_), _)).
            WillOnce(DoAll(send_functor, Return(eprosima::fastrtps::rtps::DeliveryRetCode::DELIVERED)));
    writer1.getMutex().lock();
    ASSERT_TRUE(async.add_new_sample(&writer1, &change_writer1,
            std::chrono::steady_clock::now() + std::chrono::hours(24)));
    writer1.getMutex().unlock();
    this->wait_changes_was_delivered(1);
    EXPECT_NE(std::this_thread::get_id(), this->last_thread_delivering_sample);
    this->changes_delivered.clear();

    // Testing add_new_sample. Writer will not be able to deliver it.
    auto& fail_call = EXPECT_CALL(writer1,
                    deliver_sample_nts(&change_writer1, _, Ref(writer1.async_locator_selector_), _)).
                    WillOnce(Return(eprosima::fastrtps::rtps::DeliveryRetCode::NOT_DELIVERED));
    EXPECT_CALL(writer1,
            deliver_sample_nts(&change_writer1, _, Ref(writer1.async_locator_selector_), _)).After(fail_call).
            WillOnce(DoAll(send_functor, Return(eprosima::fastrtps::rtps::DeliveryRetCode::DELIVERED)));
    writer1.getMutex().lock();
    ASSERT_TRUE(async.add_new_sample(&writer1, &change_writer1,
            std::chrono::steady_clock::now() + std::chrono::hours(24)));
    writer1.getMutex().unlock();
    this->wait_changes_was_delivered(1);
    EXPECT_NE(std::this_thread::get_id(), this->last_thread_delivering_sample);
    this->changes_delivered.clear();

    // Testing add_old_sample. Writer will be able to deliver it.
    EXPECT_CALL(writer1,
            deliver_sample_nts(&change_writer1, _, Ref(writer1.async_locator_selector_), _)).
            WillOnce(DoAll(send_functor, Return(eprosima::fastrtps::rtps::DeliveryRetCode::DELIVERED)));
    writer1.getMutex().lock();
    ASSERT_TRUE(async.add_old_sample(&writer1, &change_writer1));
    writer1.getMutex().unlock();
    this->wait_changes_was_delivered(1);
    EXPECT_NE(std::this_thread::get_id(), this->last_thread_delivering_sample);
    this->changes_delivered.clear();

    // Testing add_old_sample. Writer will not be able to deliver it.
    EXPECT_CALL(writer1,
            deliver_sample_nts(&change_writer1, _, Ref(writer1.async_locator_selector_), _)).
            WillOnce(Return(eprosima::fastrtps::rtps::DeliveryRetCode::NOT_DELIVERED)).
            WillOnce(DoAll(send_functor, Return(eprosima::fastrtps::rtps::DeliveryRetCode::DELIVERED)));
    writer1.getMutex().lock();
    ASSERT_TRUE(async.add_old_sample(&writer1, &change_writer1));
    writer1.getMutex().unlock();
    this->wait_changes_was_delivered(1);
    EXPECT_NE(std::this_thread::get_id(), this->last_thread_delivering_sample);
    this->changes_delivered.clear();

    // Testing add_old_sample with a change already enqueued.
    change_writer1.writer_info.previous = (eprosima::fastrtps::rtps::CacheChange_t*)1;
    change_writer1.writer_info.next = (eprosima::fastrtps::rtps::CacheChange_t*)1;

    // Send 10 samples using add_new_sample.
    INIT_CACHE_CHANGE(change_writer1, writer1, 1);
    INIT_CACHE_CHANGE(change_writer2, writer1, 1);
    eprosima::fastrtps::rtps::CacheChange_t change_writer3;
    INIT_CACHE_CHANGE(change_writer3, writer1, 1);
    eprosima::fastrtps::rtps::CacheChange_t change_writer4;
    INIT_CACHE_CHANGE(change_writer4, writer1, 1);
    eprosima::fastrtps::rtps::CacheChange_t change_writer5;
    INIT_CACHE_CHANGE(change_writer5, writer1, 1);
    eprosima::fastrtps::rtps::CacheChange_t change_writer6;
    INIT_CACHE_CHANGE(change_writer6, writer1, 1);
    eprosima::fastrtps::rtps::CacheChange_t change_writer7;
    INIT_CACHE_CHANGE(change_writer7, writer1, 1);
    eprosima::fastrtps::rtps::CacheChange_t change_writer8;
    INIT_CACHE_CHANGE(change_writer8, writer1, 1);
    eprosima::fastrtps::rtps::CacheChange_t change_writer9;
    INIT_CACHE_CHANGE(change_writer9, writer1, 1);
    eprosima::fastrtps::rtps::CacheChange_t change_writer10;
    INIT_CACHE_CHANGE(change_writer10, writer1, 1);

    EXPECT_CALL(writer1,
            deliver_sample_nts(&change_writer1, _, Ref(writer1.async_locator_selector_), _)).
            WillOnce(DoAll(send_functor, Return(eprosima::fastrtps::rtps::DeliveryRetCode::DELIVERED)));
    EXPECT_CALL(writer1,
            deliver_sample_nts(&change_writer2, _, Ref(writer1.async_locator_selector_), _)).
            WillOnce(DoAll(send_functor, Return(eprosima::fastrtps::rtps::DeliveryRetCode::DELIVERED)));
    EXPECT_CALL(writer1,
            deliver_sample_nts(&change_writer3, _, Ref(writer1.async_locator_selector_), _)).
            WillOnce(DoAll(send_functor, Return(eprosima::fastrtps::rtps::DeliveryRetCode::DELIVERED)));
    EXPECT_CALL(writer1,
            deliver_sample_nts(&change_writer4, _, Ref(writer1.async_locator_selector_), _)).
            WillOnce(DoAll(send_functor, Return(eprosima::fastrtps::rtps::DeliveryRetCode::DELIVERED)));
    EXPECT_CALL(writer1,
            deliver_sample_nts(&change_writer5, _, Ref(writer1.async_locator_selector_), _)).
            WillOnce(DoAll(send_functor, Return(eprosima::fastrtps::rtps::DeliveryRetCode::DELIVERED)));
    EXPECT_CALL(writer1,
            deliver_sample_nts(&change_writer6, _, Ref(writer1.async_locator_selector_), _)).
            WillOnce(DoAll(send_functor, Return(eprosima::fastrtps::rtps::DeliveryRetCode::DELIVERED)));
    EXPECT_CALL(writer1,
            deliver_sample_nts(&change_writer7, _, Ref(writer1.async_locator_selector_), _)).
            WillOnce(DoAll(send_functor, Return(eprosima::fastrtps::rtps::DeliveryRetCode::DELIVERED)));
    EXPECT_CALL(writer1,
            deliver_sample_nts(&change_writer8, _, Ref(writer1.async_locator_selector_), _)).
            WillOnce(DoAll(send_functor, Return(eprosima::fastrtps::rtps::DeliveryRetCode::DELIVERED)));
    EXPECT_CALL(writer1,
            deliver_sample_nts(&change_writer9, _, Ref(writer1.async_locator_selector_), _)).
            WillOnce(DoAll(send_functor, Return(eprosima::fastrtps::rtps::DeliveryRetCode::DELIVERED)));
    EXPECT_CALL(writer1,
            deliver_sample_nts(&change_writer10, _, Ref(writer1.async_locator_selector_), _)).
            WillOnce(DoAll(send_functor, Return(eprosima::fastrtps::rtps::DeliveryRetCode::DELIVERED)));
    writer1.getMutex().lock();
    ASSERT_TRUE(async.add_new_sample(&writer1, &change_writer1,
            std::chrono::steady_clock::now() + std::chrono::hours(24)));
    ASSERT_TRUE(async.add_new_sample(&writer1, &change_writer2,
            std::chrono::steady_clock::now() + std::chrono::hours(24)));
    ASSERT_TRUE(async.add_new_sample(&writer1, &change_writer3,
            std::chrono::steady_clock::now() + std::chrono::hours(24)));
    ASSERT_TRUE(async.add_new_sample(&writer1, &change_writer4,
            std::chrono::steady_clock::now() + std::chrono::hours(24)));
    ASSERT_TRUE(async.add_new_sample(&writer1, &change_writer5,
            std::chrono::steady_clock::now() + std::chrono::hours(24)));
    ASSERT_TRUE(async.add_new_sample(&writer1, &change_writer6,
            std::chrono::steady_clock::now() + std::chrono::hours(24)));
    ASSERT_TRUE(async.add_new_sample(&writer1, &change_writer7,
            std::chrono::steady_clock::now() + std::chrono::hours(24)));
    ASSERT_TRUE(async.add_new_sample(&writer1, &change_writer8,
            std::chrono::steady_clock::now() + std::chrono::hours(24)));
    ASSERT_TRUE(async.add_new_sample(&writer1, &change_writer9,
            std::chrono::steady_clock::now() + std::chrono::hours(24)));
    ASSERT_TRUE(async.add_new_sample(&writer1, &change_writer10,
            std::chrono::steady_clock::now() + std::chrono::hours(24)));
    writer1.getMutex().unlock();
    this->wait_changes_was_delivered(10);
    EXPECT_NE(std::this_thread::get_id(), this->last_thread_delivering_sample);
    this->changes_delivered.clear();

    // Send 10 samples using add_old_sample.
    EXPECT_CALL(writer1,
            deliver_sample_nts(&change_writer1, _, Ref(writer1.async_locator_selector_), _)).
            WillOnce(DoAll(send_functor, Return(eprosima::fastrtps::rtps::DeliveryRetCode::DELIVERED)));
    EXPECT_CALL(writer1,
            deliver_sample_nts(&change_writer2, _, Ref(writer1.async_locator_selector_), _)).
            WillOnce(DoAll(send_functor, Return(eprosima::fastrtps::rtps::DeliveryRetCode::DELIVERED)));
    EXPECT_CALL(writer1,
            deliver_sample_nts(&change_writer3, _, Ref(writer1.async_locator_selector_), _)).
            WillOnce(DoAll(send_functor, Return(eprosima::fastrtps::rtps::DeliveryRetCode::DELIVERED)));
    EXPECT_CALL(writer1,
            deliver_sample_nts(&change_writer4, _, Ref(writer1.async_locator_selector_), _)).
            WillOnce(DoAll(send_functor, Return(eprosima::fastrtps::rtps::DeliveryRetCode::DELIVERED)));
    EXPECT_CALL(writer1,
            deliver_sample_nts(&change_writer5, _, Ref(writer1.async_locator_selector_), _)).
            WillOnce(DoAll(send_functor, Return(eprosima::fastrtps::rtps::DeliveryRetCode::DELIVERED)));
    EXPECT_CALL(writer1,
            deliver_sample_nts(&change_writer6, _, Ref(writer1.async_locator_selector_), _)).
            WillOnce(DoAll(send_functor, Return(eprosima::fastrtps::rtps::DeliveryRetCode::DELIVERED)));
    EXPECT_CALL(writer1,
            deliver_sample_nts(&change_writer7, _, Ref(writer1.async_locator_selector_), _)).
            WillOnce(DoAll(send_functor, Return(eprosima::fastrtps::rtps::DeliveryRetCode::DELIVERED)));
    EXPECT_CALL(writer1,
            deliver_sample_nts(&change_writer8, _, Ref(writer1.async_locator_selector_), _)).
            WillOnce(DoAll(send_functor, Return(eprosima::fastrtps::rtps::DeliveryRetCode::DELIVERED)));
    EXPECT_CALL(writer1,
            deliver_sample_nts(&change_writer9, _, Ref(writer1.async_locator_selector_), _)).
            WillOnce(DoAll(send_functor, Return(eprosima::fastrtps::rtps::DeliveryRetCode::DELIVERED)));
    EXPECT_CALL(writer1,
            deliver_sample_nts(&change_writer10, _, Ref(writer1.async_locator_selector_), _)).
            WillOnce(DoAll(send_functor, Return(eprosima::fastrtps::rtps::DeliveryRetCode::DELIVERED)));
    writer1.getMutex().lock();
    ASSERT_TRUE(async.add_old_sample(&writer1, &change_writer1));
    ASSERT_TRUE(async.add_old_sample(&writer1, &change_writer2));
    ASSERT_TRUE(async.add_old_sample(&writer1, &change_writer3));
    ASSERT_TRUE(async.add_old_sample(&writer1, &change_writer4));
    ASSERT_TRUE(async.add_old_sample(&writer1, &change_writer5));
    ASSERT_TRUE(async.add_old_sample(&writer1, &change_writer6));
    ASSERT_TRUE(async.add_old_sample(&writer1, &change_writer7));
    ASSERT_TRUE(async.add_old_sample(&writer1, &change_writer8));
    ASSERT_TRUE(async.add_old_sample(&writer1, &change_writer9));
    ASSERT_TRUE(async.add_old_sample(&writer1, &change_writer10));
    writer1.getMutex().unlock();
    this->wait_changes_was_delivered(10);
    EXPECT_NE(std::this_thread::get_id(), this->last_thread_delivering_sample);
    this->changes_delivered.clear();

    // Remove changes after add_new_sample.
    EXPECT_CALL(writer1,
            deliver_sample_nts(_, _, Ref(writer1.async_locator_selector_), _)).
            WillOnce(DoAll(send_functor, Return(eprosima::fastrtps::rtps::DeliveryRetCode::DELIVERED))).
            WillRepeatedly(Return(eprosima::fastrtps::rtps::DeliveryRetCode::NOT_DELIVERED));
    writer1.getMutex().lock();
    ASSERT_TRUE(async.add_new_sample(&writer1, &change_writer1,
            std::chrono::steady_clock::now() + std::chrono::hours(24)));
    ASSERT_TRUE(async.add_new_sample(&writer1, &change_writer2,
            std::chrono::steady_clock::now() + std::chrono::hours(24)));
    ASSERT_TRUE(async.add_new_sample(&writer1, &change_writer3,
            std::chrono::steady_clock::now() + std::chrono::hours(24)));
    ASSERT_TRUE(async.add_new_sample(&writer1, &change_writer4,
            std::chrono::steady_clock::now() + std::chrono::hours(24)));
    ASSERT_TRUE(async.add_new_sample(&writer1, &change_writer5,
            std::chrono::steady_clock::now() + std::chrono::hours(24)));
    ASSERT_TRUE(async.add_new_sample(&writer1, &change_writer6,
            std::chrono::steady_clock::now() + std::chrono::hours(24)));
    ASSERT_TRUE(async.add_new_sample(&writer1, &change_writer7,
            std::chrono::steady_clock::now() + std::chrono::hours(24)));
    ASSERT_TRUE(async.add_new_sample(&writer1, &change_writer8,
            std::chrono::steady_clock::now() + std::chrono::hours(24)));
    ASSERT_TRUE(async.add_new_sample(&writer1, &change_writer9,
            std::chrono::steady_clock::now() + std::chrono::hours(24)));
    ASSERT_TRUE(async.add_new_sample(&writer1, &change_writer10,
            std::chrono::steady_clock::now() + std::chrono::hours(24)));
    writer1.getMutex().unlock();
    this->wait_changes_was_delivered(1);
    EXPECT_NE(std::this_thread::get_id(), this->last_thread_delivering_sample);
    this->changes_delivered.clear();
    writer1.getMutex().lock();
    ASSERT_TRUE(nullptr == change_writer1.writer_info.next &&
            nullptr == change_writer1.writer_info.previous);
    ASSERT_TRUE(nullptr != change_writer2.writer_info.next &&
            nullptr != change_writer2.writer_info.previous);
    ASSERT_TRUE(nullptr != change_writer3.writer_info.next &&
            nullptr != change_writer3.writer_info.previous);
    ASSERT_TRUE(nullptr != change_writer4.writer_info.next &&
            nullptr != change_writer4.writer_info.previous);
    ASSERT_TRUE(nullptr != change_writer5.writer_info.next &&
            nullptr != change_writer5.writer_info.previous);
    ASSERT_TRUE(nullptr != change_writer6.writer_info.next &&
            nullptr != change_writer6.writer_info.previous);
    ASSERT_TRUE(nullptr != change_writer7.writer_info.next &&
            nullptr != change_writer7.writer_info.previous);
    ASSERT_TRUE(nullptr != change_writer8.writer_info.next &&
            nullptr != change_writer8.writer_info.previous);
    ASSERT_TRUE(nullptr != change_writer9.writer_info.next &&
            nullptr != change_writer9.writer_info.previous);
    ASSERT_TRUE(nullptr != change_writer10.writer_info.next &&
            nullptr != change_writer10.writer_info.previous);
    writer1.getMutex().unlock();
    writer1.getMutex().lock();
    async.remove_change(&change_writer10);
    writer1.getMutex().unlock();
    writer1.getMutex().lock();
    async.remove_change(&change_writer9);
    writer1.getMutex().unlock();
    writer1.getMutex().lock();
    async.remove_change(&change_writer8);
    writer1.getMutex().unlock();
    writer1.getMutex().lock();
    async.remove_change(&change_writer7);
    writer1.getMutex().unlock();
    writer1.getMutex().lock();
    async.remove_change(&change_writer6);
    writer1.getMutex().unlock();
    writer1.getMutex().lock();
    async.remove_change(&change_writer5);
    writer1.getMutex().unlock();
    writer1.getMutex().lock();
    async.remove_change(&change_writer4);
    writer1.getMutex().unlock();
    writer1.getMutex().lock();
    async.remove_change(&change_writer3);
    writer1.getMutex().unlock();
    writer1.getMutex().lock();
    async.remove_change(&change_writer2);
    writer1.getMutex().unlock();
    ASSERT_TRUE(nullptr == change_writer2.writer_info.next &&
            nullptr == change_writer2.writer_info.previous);
    ASSERT_TRUE(nullptr == change_writer3.writer_info.next &&
            nullptr == change_writer3.writer_info.previous);
    ASSERT_TRUE(nullptr == change_writer4.writer_info.next &&
            nullptr == change_writer4.writer_info.previous);
    ASSERT_TRUE(nullptr == change_writer5.writer_info.next &&
            nullptr == change_writer5.writer_info.previous);
    ASSERT_TRUE(nullptr == change_writer6.writer_info.next &&
            nullptr == change_writer6.writer_info.previous);
    ASSERT_TRUE(nullptr == change_writer7.writer_info.next &&
            nullptr == change_writer7.writer_info.previous);
    ASSERT_TRUE(nullptr == change_writer8.writer_info.next &&
            nullptr == change_writer8.writer_info.previous);
    ASSERT_TRUE(nullptr == change_writer9.writer_info.next &&
            nullptr == change_writer9.writer_info.previous);
    ASSERT_TRUE(nullptr == change_writer10.writer_info.next &&
            nullptr == change_writer10.writer_info.previous);

    // Remove changes after add_old_sample.
    EXPECT_CALL(writer1,
            deliver_sample_nts(_, _, Ref(writer1.async_locator_selector_), _)).
            WillOnce(DoAll(send_functor, Return(eprosima::fastrtps::rtps::DeliveryRetCode::DELIVERED))).
            WillRepeatedly(Return(eprosima::fastrtps::rtps::DeliveryRetCode::NOT_DELIVERED));
    writer1.getMutex().lock();
    ASSERT_TRUE(async.add_old_sample(&writer1, &change_writer1));
    ASSERT_TRUE(async.add_old_sample(&writer1, &change_writer2));
    ASSERT_TRUE(async.add_old_sample(&writer1, &change_writer3));
    ASSERT_TRUE(async.add_old_sample(&writer1, &change_writer4));
    ASSERT_TRUE(async.add_old_sample(&writer1, &change_writer5));
    ASSERT_TRUE(async.add_old_sample(&writer1, &change_writer6));
    ASSERT_TRUE(async.add_old_sample(&writer1, &change_writer7));
    ASSERT_TRUE(async.add_old_sample(&writer1, &change_writer8));
    ASSERT_TRUE(async.add_old_sample(&writer1, &change_writer9));
    ASSERT_TRUE(async.add_old_sample(&writer1, &change_writer10));
    writer1.getMutex().unlock();
    this->wait_changes_was_delivered(1);
    EXPECT_NE(std::this_thread::get_id(), this->last_thread_delivering_sample);
    this->changes_delivered.clear();
    writer1.getMutex().lock();
    ASSERT_TRUE(nullptr == change_writer1.writer_info.next &&
            nullptr == change_writer1.writer_info.previous);
    ASSERT_TRUE(nullptr != change_writer2.writer_info.next &&
            nullptr != change_writer2.writer_info.previous);
    ASSERT_TRUE(nullptr != change_writer3.writer_info.next &&
            nullptr != change_writer3.writer_info.previous);
    ASSERT_TRUE(nullptr != change_writer4.writer_info.next &&
            nullptr != change_writer4.writer_info.previous);
    ASSERT_TRUE(nullptr != change_writer5.writer_info.next &&
            nullptr != change_writer5.writer_info.previous);
    ASSERT_TRUE(nullptr != change_writer6.writer_info.next &&
            nullptr != change_writer6.writer_info.previous);
    ASSERT_TRUE(nullptr != change_writer7.writer_info.next &&
            nullptr != change_writer7.writer_info.previous);
    ASSERT_TRUE(nullptr != change_writer8.writer_info.next &&
            nullptr != change_writer8.writer_info.previous);
    ASSERT_TRUE(nullptr != change_writer9.writer_info.next &&
            nullptr != change_writer9.writer_info.previous);
    ASSERT_TRUE(nullptr != change_writer10.writer_info.next &&
            nullptr != change_writer10.writer_info.previous);
    writer1.getMutex().unlock();
    writer1.getMutex().lock();
    async.remove_change(&change_writer10);
    writer1.getMutex().unlock();
    writer1.getMutex().lock();
    async.remove_change(&change_writer9);
    writer1.getMutex().unlock();
    writer1.getMutex().lock();
    async.remove_change(&change_writer8);
    writer1.getMutex().unlock();
    writer1.getMutex().lock();
    async.remove_change(&change_writer7);
    writer1.getMutex().unlock();
    writer1.getMutex().lock();
    async.remove_change(&change_writer6);
    writer1.getMutex().unlock();
    writer1.getMutex().lock();
    async.remove_change(&change_writer5);
    writer1.getMutex().unlock();
    writer1.getMutex().lock();
    async.remove_change(&change_writer4);
    writer1.getMutex().unlock();
    writer1.getMutex().lock();
    async.remove_change(&change_writer3);
    writer1.getMutex().unlock();
    writer1.getMutex().lock();
    async.remove_change(&change_writer2);
    writer1.getMutex().unlock();
    ASSERT_TRUE(nullptr == change_writer2.writer_info.next &&
            nullptr == change_writer2.writer_info.previous);
    ASSERT_TRUE(nullptr == change_writer3.writer_info.next &&
            nullptr == change_writer3.writer_info.previous);
    ASSERT_TRUE(nullptr == change_writer4.writer_info.next &&
            nullptr == change_writer4.writer_info.previous);
    ASSERT_TRUE(nullptr == change_writer5.writer_info.next &&
            nullptr == change_writer5.writer_info.previous);
    ASSERT_TRUE(nullptr == change_writer6.writer_info.next &&
            nullptr == change_writer6.writer_info.previous);
    ASSERT_TRUE(nullptr == change_writer7.writer_info.next &&
            nullptr == change_writer7.writer_info.previous);
    ASSERT_TRUE(nullptr == change_writer8.writer_info.next &&
            nullptr == change_writer8.writer_info.previous);
    ASSERT_TRUE(nullptr == change_writer9.writer_info.next &&
            nullptr == change_writer9.writer_info.previous);
    ASSERT_TRUE(nullptr == change_writer10.writer_info.next &&
            nullptr == change_writer10.writer_info.previous);

    // Sending 10 samples applying limitations with add_new_sample.
    EXPECT_CALL(writer1,
            deliver_sample_nts(&change_writer1, _, Ref(writer1.async_locator_selector_), _)).
            WillOnce(Return(eprosima::fastrtps::rtps::DeliveryRetCode::EXCEEDED_LIMIT)).
            WillOnce(DoAll(send_functor, Return(eprosima::fastrtps::rtps::DeliveryRetCode::DELIVERED)));
    EXPECT_CALL(writer1,
            deliver_sample_nts(&change_writer2, _, Ref(writer1.async_locator_selector_), _)).
            WillOnce(Return(eprosima::fastrtps::rtps::DeliveryRetCode::EXCEEDED_LIMIT)).
            WillOnce(DoAll(send_functor, Return(eprosima::fastrtps::rtps::DeliveryRetCode::DELIVERED)));
    EXPECT_CALL(writer1,
            deliver_sample_nts(&change_writer3, _, Ref(writer1.async_locator_selector_), _)).
            WillOnce(Return(eprosima::fastrtps::rtps::DeliveryRetCode::EXCEEDED_LIMIT)).
            WillOnce(DoAll(send_functor, Return(eprosima::fastrtps::rtps::DeliveryRetCode::DELIVERED)));
    EXPECT_CALL(writer1,
            deliver_sample_nts(&change_writer4, _, Ref(writer1.async_locator_selector_), _)).
            WillOnce(Return(eprosima::fastrtps::rtps::DeliveryRetCode::EXCEEDED_LIMIT)).
            WillOnce(DoAll(send_functor, Return(eprosima::fastrtps::rtps::DeliveryRetCode::DELIVERED)));
    EXPECT_CALL(writer1,
            deliver_sample_nts(&change_writer5, _, Ref(writer1.async_locator_selector_), _)).
            WillOnce(Return(eprosima::fastrtps::rtps::DeliveryRetCode::EXCEEDED_LIMIT)).
            WillOnce(DoAll(send_functor, Return(eprosima::fastrtps::rtps::DeliveryRetCode::DELIVERED)));
    EXPECT_CALL(writer1,
            deliver_sample_nts(&change_writer6, _, Ref(writer1.async_locator_selector_), _)).
            WillOnce(Return(eprosima::fastrtps::rtps::DeliveryRetCode::EXCEEDED_LIMIT)).
            WillOnce(DoAll(send_functor, Return(eprosima::fastrtps::rtps::DeliveryRetCode::DELIVERED)));
    EXPECT_CALL(writer1,
            deliver_sample_nts(&change_writer7, _, Ref(writer1.async_locator_selector_), _)).
            WillOnce(Return(eprosima::fastrtps::rtps::DeliveryRetCode::EXCEEDED_LIMIT)).
            WillOnce(DoAll(send_functor, Return(eprosima::fastrtps::rtps::DeliveryRetCode::DELIVERED)));
    EXPECT_CALL(writer1,
            deliver_sample_nts(&change_writer8, _, Ref(writer1.async_locator_selector_), _)).
            WillOnce(Return(eprosima::fastrtps::rtps::DeliveryRetCode::EXCEEDED_LIMIT)).
            WillOnce(DoAll(send_functor, Return(eprosima::fastrtps::rtps::DeliveryRetCode::DELIVERED)));
    EXPECT_CALL(writer1,
            deliver_sample_nts(&change_writer9, _, Ref(writer1.async_locator_selector_), _)).
            WillOnce(Return(eprosima::fastrtps::rtps::DeliveryRetCode::EXCEEDED_LIMIT)).
            WillOnce(DoAll(send_functor, Return(eprosima::fastrtps::rtps::DeliveryRetCode::DELIVERED)));
    EXPECT_CALL(writer1,
            deliver_sample_nts(&change_writer10, _, Ref(writer1.async_locator_selector_), _)).
            WillOnce(Return(eprosima::fastrtps::rtps::DeliveryRetCode::EXCEEDED_LIMIT)).
            WillOnce(DoAll(send_functor, Return(eprosima::fastrtps::rtps::DeliveryRetCode::DELIVERED)));
    EXPECT_CALL(*FlowControllerLimitedAsyncPublishModeMock::get_group(),
            get_current_bytes_processed()).
            WillRepeatedly(Return(10100));
    writer1.getMutex().lock();
    ASSERT_TRUE(async.add_new_sample(&writer1, &change_writer1,
            std::chrono::steady_clock::now() + std::chrono::hours(24)));
    writer1.getMutex().unlock();
    EXPECT_CALL(*FlowControllerLimitedAsyncPublishModeMock::get_group(),
            get_current_bytes_processed()).
            WillRepeatedly(Return(0));
    this->wait_changes_was_delivered(1);
    EXPECT_NE(std::this_thread::get_id(), this->last_thread_delivering_sample);
    EXPECT_CALL(*FlowControllerLimitedAsyncPublishModeMock::get_group(),
            get_current_bytes_processed()).
            WillRepeatedly(Return(10100));
    writer1.getMutex().lock();
    ASSERT_TRUE(async.add_new_sample(&writer1, &change_writer2,
            std::chrono::steady_clock::now() + std::chrono::hours(24)));
    writer1.getMutex().unlock();
    EXPECT_CALL(*FlowControllerLimitedAsyncPublishModeMock::get_group(),
            get_current_bytes_processed()).
            WillRepeatedly(Return(0));
    this->wait_changes_was_delivered(2);
    EXPECT_NE(std::this_thread::get_id(), this->last_thread_delivering_sample);
    EXPECT_CALL(*FlowControllerLimitedAsyncPublishModeMock::get_group(),
            get_current_bytes_processed()).
            WillRepeatedly(Return(10100));
    writer1.getMutex().lock();
    ASSERT_TRUE(async.add_new_sample(&writer1, &change_writer3,
            std::chrono::steady_clock::now() + std::chrono::hours(24)));
    writer1.getMutex().unlock();
    EXPECT_CALL(*FlowControllerLimitedAsyncPublishModeMock::get_group(),
            get_current_bytes_processed()).
            WillRepeatedly(Return(0));
    this->wait_changes_was_delivered(3);
    EXPECT_NE(std::this_thread::get_id(), this->last_thread_delivering_sample);
    EXPECT_CALL(*FlowControllerLimitedAsyncPublishModeMock::get_group(),
            get_current_bytes_processed()).
            WillRepeatedly(Return(10100));
    writer1.getMutex().lock();
    ASSERT_TRUE(async.add_new_sample(&writer1, &change_writer4,
            std::chrono::steady_clock::now() + std::chrono::hours(24)));
    writer1.getMutex().unlock();
    EXPECT_CALL(*FlowControllerLimitedAsyncPublishModeMock::get_group(),
            get_current_bytes_processed()).
            WillRepeatedly(Return(0));
    this->wait_changes_was_delivered(4);
    EXPECT_NE(std::this_thread::get_id(), this->last_thread_delivering_sample);
    writer1.getMutex().lock();
    ASSERT_TRUE(async.add_new_sample(&writer1, &change_writer5,
            std::chrono::steady_clock::now() + std::chrono::hours(24)));
    ASSERT_TRUE(async.add_new_sample(&writer1, &change_writer6,
            std::chrono::steady_clock::now() + std::chrono::hours(24)));
    ASSERT_TRUE(async.add_new_sample(&writer1, &change_writer7,
            std::chrono::steady_clock::now() + std::chrono::hours(24)));
    ASSERT_TRUE(async.add_new_sample(&writer1, &change_writer8,
            std::chrono::steady_clock::now() + std::chrono::hours(24)));
    ASSERT_TRUE(async.add_new_sample(&writer1, &change_writer9,
            std::chrono::steady_clock::now() + std::chrono::hours(24)));
    ASSERT_TRUE(async.add_new_sample(&writer1, &change_writer10,
            std::chrono::steady_clock::now() + std::chrono::hours(24)));
    writer1.getMutex().unlock();
    this->wait_changes_was_delivered(10);
    EXPECT_NE(std::this_thread::get_id(), this->last_thread_delivering_sample);
    this->changes_delivered.clear();

    // Sending 10 samples applying limitations with add_old_sample.
    EXPECT_CALL(writer1,
            deliver_sample_nts(&change_writer1, _, Ref(writer1.async_locator_selector_), _)).
            WillOnce(Return(eprosima::fastrtps::rtps::DeliveryRetCode::EXCEEDED_LIMIT)).
            WillOnce(DoAll(send_functor, Return(eprosima::fastrtps::rtps::DeliveryRetCode::DELIVERED)));
    EXPECT_CALL(writer1,
            deliver_sample_nts(&change_writer2, _, Ref(writer1.async_locator_selector_), _)).
            WillOnce(Return(eprosima::fastrtps::rtps::DeliveryRetCode::EXCEEDED_LIMIT)).
            WillOnce(DoAll(send_functor, Return(eprosima::fastrtps::rtps::DeliveryRetCode::DELIVERED)));
    EXPECT_CALL(writer1,
            deliver_sample_nts(&change_writer3, _, Ref(writer1.async_locator_selector_), _)).
            WillOnce(Return(eprosima::fastrtps::rtps::DeliveryRetCode::EXCEEDED_LIMIT)).
            WillOnce(DoAll(send_functor, Return(eprosima::fastrtps::rtps::DeliveryRetCode::DELIVERED)));
    EXPECT_CALL(writer1,
            deliver_sample_nts(&change_writer4, _, Ref(writer1.async_locator_selector_), _)).
            WillOnce(Return(eprosima::fastrtps::rtps::DeliveryRetCode::EXCEEDED_LIMIT)).
            WillOnce(DoAll(send_functor, Return(eprosima::fastrtps::rtps::DeliveryRetCode::DELIVERED)));
    EXPECT_CALL(writer1,
            deliver_sample_nts(&change_writer5, _, Ref(writer1.async_locator_selector_), _)).
            WillOnce(Return(eprosima::fastrtps::rtps::DeliveryRetCode::EXCEEDED_LIMIT)).
            WillOnce(DoAll(send_functor, Return(eprosima::fastrtps::rtps::DeliveryRetCode::DELIVERED)));
    EXPECT_CALL(writer1,
            deliver_sample_nts(&change_writer6, _, Ref(writer1.async_locator_selector_), _)).
            WillOnce(Return(eprosima::fastrtps::rtps::DeliveryRetCode::EXCEEDED_LIMIT)).
            WillOnce(DoAll(send_functor, Return(eprosima::fastrtps::rtps::DeliveryRetCode::DELIVERED)));
    EXPECT_CALL(writer1,
            deliver_sample_nts(&change_writer7, _, Ref(writer1.async_locator_selector_), _)).
            WillOnce(Return(eprosima::fastrtps::rtps::DeliveryRetCode::EXCEEDED_LIMIT)).
            WillOnce(DoAll(send_functor, Return(eprosima::fastrtps::rtps::DeliveryRetCode::DELIVERED)));
    EXPECT_CALL(writer1,
            deliver_sample_nts(&change_writer8, _, Ref(writer1.async_locator_selector_), _)).
            WillOnce(Return(eprosima::fastrtps::rtps::DeliveryRetCode::EXCEEDED_LIMIT)).
            WillOnce(DoAll(send_functor, Return(eprosima::fastrtps::rtps::DeliveryRetCode::DELIVERED)));
    EXPECT_CALL(writer1,
            deliver_sample_nts(&change_writer9, _, Ref(writer1.async_locator_selector_), _)).
            WillOnce(Return(eprosima::fastrtps::rtps::DeliveryRetCode::EXCEEDED_LIMIT)).
            WillOnce(DoAll(send_functor, Return(eprosima::fastrtps::rtps::DeliveryRetCode::DELIVERED)));
    EXPECT_CALL(writer1,
            deliver_sample_nts(&change_writer10, _, Ref(writer1.async_locator_selector_), _)).
            WillOnce(Return(eprosima::fastrtps::rtps::DeliveryRetCode::EXCEEDED_LIMIT)).
            WillOnce(DoAll(send_functor, Return(eprosima::fastrtps::rtps::DeliveryRetCode::DELIVERED)));
    EXPECT_CALL(*FlowControllerLimitedAsyncPublishModeMock::get_group(),
            get_current_bytes_processed()).
            WillRepeatedly(Return(10100));
    writer1.getMutex().lock();
    ASSERT_TRUE(async.add_old_sample(&writer1, &change_writer1));
    writer1.getMutex().unlock();
    EXPECT_CALL(*FlowControllerLimitedAsyncPublishModeMock::get_group(),
            get_current_bytes_processed()).
            WillRepeatedly(Return(0));
    this->wait_changes_was_delivered(1);
    EXPECT_NE(std::this_thread::get_id(), this->last_thread_delivering_sample);
    EXPECT_CALL(*FlowControllerLimitedAsyncPublishModeMock::get_group(),
            get_current_bytes_processed()).
            WillRepeatedly(Return(10100));
    writer1.getMutex().lock();
    ASSERT_TRUE(async.add_old_sample(&writer1, &change_writer2));
    writer1.getMutex().unlock();
    EXPECT_CALL(*FlowControllerLimitedAsyncPublishModeMock::get_group(),
            get_current_bytes_processed()).
            WillRepeatedly(Return(0));
    this->wait_changes_was_delivered(2);
    EXPECT_NE(std::this_thread::get_id(), this->last_thread_delivering_sample);
    EXPECT_CALL(*FlowControllerLimitedAsyncPublishModeMock::get_group(),
            get_current_bytes_processed()).
            WillRepeatedly(Return(10100));
    writer1.getMutex().lock();
    ASSERT_TRUE(async.add_old_sample(&writer1, &change_writer3));
    writer1.getMutex().unlock();
    EXPECT_CALL(*FlowControllerLimitedAsyncPublishModeMock::get_group(),
            get_current_bytes_processed()).
            WillRepeatedly(Return(0));
    this->wait_changes_was_delivered(3);
    EXPECT_NE(std::this_thread::get_id(), this->last_thread_delivering_sample);
    EXPECT_CALL(*FlowControllerLimitedAsyncPublishModeMock::get_group(),
            get_current_bytes_processed()).
            WillRepeatedly(Return(10100));
    writer1.getMutex().lock();
    ASSERT_TRUE(async.add_old_sample(&writer1, &change_writer4));
    writer1.getMutex().unlock();
    EXPECT_CALL(*FlowControllerLimitedAsyncPublishModeMock::get_group(),
            get_current_bytes_processed()).
            WillRepeatedly(Return(0));
    this->wait_changes_was_delivered(4);
    EXPECT_NE(std::this_thread::get_id(), this->last_thread_delivering_sample);
    writer1.getMutex().lock();
    ASSERT_TRUE(async.add_old_sample(&writer1, &change_writer5));
    ASSERT_TRUE(async.add_old_sample(&writer1, &change_writer6));
    ASSERT_TRUE(async.add_old_sample(&writer1, &change_writer7));
    ASSERT_TRUE(async.add_old_sample(&writer1, &change_writer8));
    ASSERT_TRUE(async.add_old_sample(&writer1, &change_writer9));
    ASSERT_TRUE(async.add_old_sample(&writer1, &change_writer10));
    writer1.getMutex().unlock();
    this->wait_changes_was_delivered(10);
    EXPECT_NE(std::this_thread::get_id(), this->last_thread_delivering_sample);
    this->changes_delivered.clear();

    async.unregister_writer(&writer1);
}


text:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Word = Microsoft.Office.Interop.Word;

namespace ZS.WordAddIn
{
    //internal class WordHelper
    //{

    //    /// <summary>
    //    /// 移除文档里面所有备注
    //    /// </summary>
    //    public static void RemoveAllComments(Word.Document doc)
    //    {
    //        if (doc == null) return;

    //        doc.DeleteAllComments();
    //    }

    //    public static void RemoveAllVisibleComments(Word.Document doc)
    //    {
    //        if (doc == null) return;

    //        doc.DeleteAllCommentsShown();
    //    }

    //    public static Int32 GetParagraphsCount(Word.Document doc)
    //    {
    //        return doc.Paragraphs.Count;

    //    }

    //    public class Section
    //    {

    //        public Word.Range Rang { get; set; }
    //        public string Text { get; set; }
    //        public string KeyText { get; set; }

    //        public override string ToString()
    //        {
    //            if (KeyText != null)
    //            {
    //                return KeyText.ToString();
    //            }
    //            else
    //            {
    //                return base.ToString();
    //            }
    //        }

    //        public static List<Section> GetAll(Word.Document doc)
    //        {
    //            if (doc == null) return null;

    //            List<Section> list = new List<Section>();

    //            if (doc.Sections != null && doc.Sections.Count > 0)
    //            {
    //                foreach (Word.Section sec in doc.Sections)
    //                {
    //                    Section s = new Section();

    //                    s.Rang = sec.Range;   // 区域对象
    //                    s.Text = sec.Range.Text;  // 区域文本内容
    //                    if (s.Text.Length > 5)  // 区域关键字(前五个)
    //                    {
    //                        s.KeyText = s.Text.Substring(0, 5);
    //                    }
    //                    else
    //                    {
    //                        s.KeyText = s.Text;
    //                    }

    //                    list.Add(s);
    //                }
    //                return list;
    //            }
    //            else
    //            {
    //                return null;
    //            }
    //        }
    //    }

    //    public class Paragraph
    //    {
    //        public Word.Range Rang { get; set; }
    //        public string Text { get; set; }
    //        public string KeyText { get; set; }

    //        public override string ToString()
    //        {
    //            if (KeyText != null)
    //            {
    //                return KeyText.ToString();
    //            }
    //            else
    //            {
    //                return base.ToString();
    //            }
    //        }

    //        public static List<Paragraph> GetAll(Word.Document doc)
    //        {
    //            if (doc == null) return null;

    //            List<Paragraph> list = new List<Paragraph>();

    //            if (doc.Paragraphs != null && doc.Paragraphs.Count > 0)
    //            {
    //                foreach (Word.Paragraph p in doc.Paragraphs)
    //                {
    //                    Paragraph s = new Paragraph();

    //                    s.Rang = p.Range;   // 区域对象
    //                    s.Text = p.Range.Text;  // 区域文本内容
    //                    if (s.Text.Length > 5)  // 区域关键字(前五个)
    //                    {
    //                        s.KeyText = s.Text.Substring(0, 5);
    //                    }
    //                    else
    //                    {
    //                        s.KeyText = s.Text;
    //                    }

    //                    list.Add(s);
    //                }
    //                return list;
    //            }
    //            else
    //            {
    //                return null;
    //            }
    //        }
    //    }

    //}

    /// <summary>
    /// 文档处理帮助
    /// </summary>
    internal class DocumentHelper
    {
        // Word文档对象
        Word.Document m_Doc = null;

        public DocumentHelper(Word.Document doc)
        {
            if (doc == null) throw new ApplicationException("未指定文档对象!");
            m_Doc = doc;
        }

        #region 段落

        /// <summary>
        /// 获取所有段落信息
        /// </summary>
        /// <returns></returns>
        public List<Paragraph> Paragraph_GetAll()
        {

            List<Paragraph> list = new List<Paragraph>();

            if (m_Doc.Paragraphs != null && m_Doc.Paragraphs.Count > 0)
            {
                foreach (Word.Paragraph p in m_Doc.Paragraphs)
                {
                    Paragraph s = new Paragraph(p.Range);

                    list.Add(s);
                }
                return list;
            }
            else
            {
                return null;
            }
        }

        public class Paragraph
        {

            public Paragraph() { }
            public Paragraph(Word.Range rng)
            {
                if (rng == null)
                {
                    throw new ApplicationException("未指定的Range对象!");
                }

                this.Rang = rng;
                this.Text = rng.Text;
                if (this.Text.Length > KeyTextLength)
                {
                    this.KeyText = this.Text.Substring(0, this.KeyTextLength);
                }
                else
                {
                    this.KeyText = this.Text;
                }

                if (_propTrimKeyText)
                {
                    this.KeyText = this.KeyText.Trim();
                }
            }

            /// <summary>
            /// 段落的Range对象
            /// </summary>
            public Word.Range Rang { get; set; }
            /// <summary>
            /// 获取或者设置段落的全部文本
            /// </summary>
            public string Text { get; set; }
            /// <summary>
            /// 获取或者设置段落的关键文字
            /// </summary>
            public string KeyText { get; set; }

            private Int32 _propKeyTextLength = 10;
            /// <summary>
            /// 获取或者设置段落关键文字表示的长度
            /// </summary>
            public Int32 KeyTextLength
            {

                get { return _propKeyTextLength; }
                set { _propKeyTextLength = value; }
            }

            private Boolean _propTrimKeyText = true;
            /// <summary>
            /// 是否移除关键字前后的空格
            /// </summary>
            public Boolean TrimKeyText
            {
                get { return _propTrimKeyText; }
                set
                {
                    _propTrimKeyText = value;
                    if (_propTrimKeyText)
                    {
                        if (KeyText != null) KeyText.Trim();
                    }
                }
            }

            public override string ToString()
            {
                if (KeyText != null)
                {
                    return KeyText.ToString();
                }
                else
                {
                    return base.ToString();
                }
            }

        }

        #endregion

        #region 小节

        #endregion

        #region Shapes

        /// <summary>
        /// 获取文档中的所有图片
        /// </summary>
        /// <returns>返回图片对象集合</returns>
        public List<System.Drawing.Image> Shape_GetAllPicture()
        {

            List<System.Drawing.Image> listR = new List<System.Drawing.Image>();
            // 读取Shape中的图片
            if (m_Doc.Shapes != null && m_Doc.Shapes.Count > 0)
            {
                foreach (Word.Shape p in m_Doc.Shapes)
                {
                    if (p.Type == Microsoft.Office.Core.MsoShapeType.msoPicture)
                    {
                        var img = ConvertToImage(p);
                        if (img != null)
                        {
                            listR.Add(img);
                        }

                    }
                }
            }

            // 读取InLineShape中的图片
            if (m_Doc.InlineShapes != null && m_Doc.InlineShapes.Count > 0)
            {
                foreach (Word.InlineShape p in m_Doc.InlineShapes)
                {
                    if (p.Type == Word.WdInlineShapeType.wdInlineShapePicture)
                    {
                        var img = ConvertToImage(p);
                        if (img != null)
                        {
                            listR.Add(img);
                        }

                    }
                }
            }

            if (listR.Count > 0)
            {
                return listR;
            }
            else
            {
                return null;
            }
        }

        #region Shape转图像
        /// <summary>
        /// 将Shape对象转换为Image
        /// </summary>
        /// <param name="shape"></param>
        /// <returns></returns>
        private System.Drawing.Image ConvertToImage(Word.Shape shape)
        {
            if (shape == null) return null;
            shape.Select();
            m_Doc.Application.Selection.Copy();
            //Globals.ThisAddIn.Application.Selection.Copy();

            if (System.Windows.Forms.Clipboard.GetDataObject() != null)
            {
                var data = System.Windows.Forms.Clipboard.GetDataObject();
                if (data != null && data.GetDataPresent(System.Windows.Forms.DataFormats.Bitmap))
                {
                    System.Drawing.Image img = (System.Drawing.Image)data.GetData(System.Windows.Forms.DataFormats.Bitmap, true);
                    return img;
                }
            }
            return null;
        }

        /// <summary>
        /// 将InlineShape转换为Image
        /// </summary>
        /// <param name="shape"></param>
        /// <returns></returns>
        private System.Drawing.Image ConvertToImage(Word.InlineShape shape)
        {
            if (shape == null) return null;
            shape.Select();
            m_Doc.Application.Selection.Copy();
            //Globals.ThisAddIn.Application.Selection.Copy();

            if (System.Windows.Forms.Clipboard.GetDataObject() != null)
            {
                var data = System.Windows.Forms.Clipboard.GetDataObject();
                if (data != null && data.GetDataPresent(System.Windows.Forms.DataFormats.Bitmap))
                {
                    System.Drawing.Image img = (System.Drawing.Image)data.GetData(System.Windows.Forms.DataFormats.Bitmap, true);
                    return img;
                }
            }
            return null;
        }

        #endregion

        public class ShapePicture
        {
            public Word.Shape ShapeOrg { get; set; }
        }

        #endregion

        #region 文档保存与另存

        /// <summary>
        /// 将文档以PDF格式另存到指定目录。
        /// </summary>
        /// <param name="savePath"></param>
        /// <returns></returns>
        public Boolean SaveAsPDF(string savePath)
        {
            throw new ApplicationException("方法未做处理!");
        }

        /// <summary>
        /// 将文档以PDF格式保存到文档所在的目录,以文档名称命名。如果文档的路径不存在,则保存到桌面
        /// </summary>
        /// <returns></returns>
        public Boolean SaveAsPDF()
        {
            string folder = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
            if (m_Doc.Path != null && m_Doc.Path != string.Empty)
            {
                folder = m_Doc.Path;
            }

            if (!folder.EndsWith(@"\"))
            {
                folder = folder + @"\";
            }

            string fileName = m_Doc.Name + ".pdf";
            if (fileName == null && fileName == string.Empty)
            {
                fileName = System.DateTime.Now.ToString("yyyyMMddHHmmss") + ".pdf";
            }

            return this.SaveAsPDF(folder + fileName, false);
        }

        private Boolean SaveAsPDF(string path, bool open)
        {
            try
            {
                object fileName = path;
                object fileFormat = Microsoft.Office.Interop.Word.WdSaveFormat.wdFormatPDF;
                m_Doc.SaveAs2(ref fileName, ref fileFormat);
                return true;
            }
            catch (Exception)
            {
                return false;
            }
        }

        public static Boolean SaveAsPDF(Word.Document doc)
        {
            // 文档验证
            if (doc == null) return false;
            string fullPath = "";
            return SaveAsPDF(doc, doc.Path, false, ref fullPath);
        }

        public static Boolean SaveAsPDF(Word.Document pDoc, String pFolder, Boolean pOpen,ref string pFullPath)
        {
            object fileName = System.DateTime.Now.ToString("yyyyMMddHHmmss") + ".pdf";
            object fileFormat = Microsoft.Office.Interop.Word.WdSaveFormat.wdFormatPDF;
            object fileFullPath = string.Empty;

            // 文档验证
            if (pDoc == null) return false;

            // 目录不正确则指定到桌面
            if (pFolder == null || pFolder == string.Empty || !System.IO.Directory.Exists(pFolder))
            {
                pFolder = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
            }
            if (!pFolder.EndsWith(@"\"))
            {
                pFolder = pFolder + @"\";
            }

            // 文档名称
            if (pDoc.Name != null && pDoc.Name != string.Empty)
            {
                if (pDoc.Name.Contains("."))
                {
                    fileName = pDoc.Name.Substring(0, pDoc.Name.LastIndexOf(".")) + ".pdf";
                }
                else
                {
                    fileName = pDoc.Name + ".pdf";
                }
            }

            pFullPath = pFolder + fileName;
            fileFullPath = pFullPath;
            try
            {
                pDoc.SaveAs2(ref fileFullPath, ref fileFormat);
                return true;
            }
            catch (Exception)
            {
                return false;
            }
        }

        #endregion

        #region 静态方法

        public static void SaveShapeToFile(Word.Shape shape )
        {
            if (shape == null) return;
            shape.Select();
            Globals.ThisAddIn.Application.Selection.Copy();

            if (System.Windows.Forms.Clipboard.GetDataObject() != null)
            {
                var data = System.Windows.Forms.Clipboard.GetDataObject();
                if (data != null && data.GetDataPresent(System.Windows.Forms.DataFormats.Bitmap))
                {
                    System.Drawing.Image img = (System.Drawing.Image)data.GetData(System.Windows.Forms.DataFormats.Bitmap, true);
                    img.Save(@"d:\TT.jpg");
                }

            }
        }

        #endregion

    }
}

redpajama_set_name: RedPajamaGithub


text:
cask 'joinme' do
  version :latest
  sha256 :no_check

  url 'https://secure.join.me/Download.aspx?installer=mac&webdownload=true'
  name 'join.me'
  homepage 'https://join.me/'
  license :gratis

  app 'join.me.app'
end


text:
<?php

declare(strict_types=1);

namespace Fazland\SkebbyRestClient\Runtime;

interface RuntimeInterface
{
    /**
     * Checks if the class has been defined.
     */
    public function classExists(string $fqcn, bool $autoload = true): bool;

    /**
     * Find out whether an extension is loaded.
     */
    public function extensionLoaded(string $extension): bool;
}