本文整理汇总了C++中abort_callback类的典型用法代码示例。如果您正苦于以下问题:C++ abort_callback类的具体用法?C++ abort_callback怎么用?C++ abort_callback使用的例子?那么恭喜您, 这里精选的类代码示例或许可以为您提供帮助。
在下文中一共展示了abort_callback类的20个代码示例,这些例子默认根据受欢迎程度排序。您可以为喜欢或者感觉有用的代码点赞,您的评价将有助于我们的系统推荐出更棒的C++代码示例。
示例1: writeOverlappedPass
void writeOverlappedPass(HANDLE handle, HANDLE myEvent, t_filesize position, const void * in,DWORD inBytes, abort_callback & abort) {
abort.check();
if (inBytes == 0) return;
OVERLAPPED ol = {};
fillOverlapped(ol, myEvent, position);
ResetEvent(myEvent);
DWORD bytesWritten;
SetLastError(NO_ERROR);
if (WriteFile( handle, in, inBytes, &bytesWritten, &ol)) {
// succeeded already?
if (bytesWritten != inBytes) throw exception_io();
return;
}
{
const DWORD code = GetLastError();
if (code != ERROR_IO_PENDING) exception_io_from_win32(code);
}
const HANDLE handles[] = {myEvent, abort.get_abort_event()};
SetLastError(NO_ERROR);
DWORD state = WaitForMultipleObjects(_countof(handles), handles, FALSE, INFINITE);
if (state == WAIT_OBJECT_0) {
try {
WIN32_IO_OP( GetOverlappedResult(handle,&ol,&bytesWritten,TRUE) );
} catch(...) {
CancelIo(handle);
throw;
}
if (bytesWritten != inBytes) throw exception_io();
return;
}
CancelIo(handle);
throw exception_aborted();
}
开发者ID:Irwin1138,项目名称:foo_bestversion,代码行数:34,代码来源:file_win32_wrapper.cpp
示例2: run
void run(threaded_process_status & p_status,abort_callback & p_abort) {
try {
const t_uint32 decode_flags = input_flag_no_seeking | input_flag_no_looping; // tell the decoders that we won't seek and that we don't want looping on formats that support looping.
input_helper input;
audio_hash.set_count(m_items.get_size());
for(t_size walk = 0; walk < m_items.get_size(); ++walk) {
p_abort.check(); // in case the input we're working with fails at doing this
p_status.set_progress(walk, m_items.get_size());
p_status.set_progress_secondary(0);
p_status.set_item_path( m_items[walk]->get_path() );
input.open(NULL, m_items[walk], decode_flags, p_abort);
double length;
{ // fetch the track length for proper dual progress display;
file_info_impl info;
// input.open should have preloaded relevant info, no need to query the input itself again.
// Regular get_info() may not retrieve freshly loaded info yet at this point (it will start giving the new info when relevant info change callbacks are dispatched); we need to use get_info_async.
if (m_items[walk]->get_info_async(info)) length = info.get_length();
else length = 0;
}
memset( &ctx, 0, sizeof( sha1_context ) );
sha1_starts( &ctx );
audio_chunk_impl_temporary l_chunk;
double decoded = 0;
while(input.run(l_chunk, p_abort)) { // main decode loop
sha1_update(&ctx,(unsigned char*)l_chunk.get_data(),l_chunk.get_data_length());
//m_peak = l_chunk.get_peak(m_peak);
if (length > 0) { // don't bother for unknown length tracks
decoded += l_chunk.get_duration();
if (decoded > length) decoded = length;
p_status.set_progress_secondary_float(decoded / length);
}
p_abort.check(); // in case the input we're working with fails at doing this
}
unsigned char sha1sum[20] ={0};
sha1_finish(&ctx,sha1sum);
pfc::string_formatter msg;
for(int i = 0; i < 20; i++ )
{
msg <<pfc::format_hex(sha1sum[i]);
audio_hash[walk] =msg;
}
}
} catch(std::exception const & e) {
m_failMsg = e.what();
}
}
开发者ID:Crazybond,项目名称:foobar2000,代码行数:55,代码来源:foo_audiohasher.cpp
示例3: AcquireByHandle
void CMutex::AcquireByHandle( HANDLE hMutex, abort_callback & aborter ) {
SetLastError(0);
HANDLE hWait[2] = {hMutex, aborter.get_abort_event()};
switch(WaitForMultipleObjects( 2, hWait, FALSE, INFINITE ) ) {
case WAIT_FAILED:
WIN32_OP_FAIL_CRITICAL("WaitForSingleObject");
case WAIT_OBJECT_0:
return;
case WAIT_OBJECT_0 + 1:
PFC_ASSERT( aborter.is_aborting() );
throw exception_aborted();
default:
uBugCheck();
}
}
开发者ID:Irwin1138,项目名称:foo_bestversion,代码行数:15,代码来源:win32_misc.cpp
示例4: MultiWaitAbortable_MsgLoop
t_size MultiWaitAbortable_MsgLoop(const HANDLE * ev, t_size evCount, abort_callback & abort) {
pfc::array_t<HANDLE> handles; handles.set_size(evCount + 1);
handles[0] = abort.get_abort_event();
pfc::memcpy_t(handles.get_ptr() + 1, ev, evCount);
for(;;) {
SetLastError(0);
const DWORD status = MsgWaitForMultipleObjects(handles.get_count(), handles.get_ptr(), FALSE, INFINITE, QS_ALLINPUT);
switch(status) {
case WAIT_TIMEOUT:
PFC_ASSERT(!"How did we get here?");
uBugCheck();
case WAIT_OBJECT_0:
throw exception_aborted();
case WAIT_FAILED:
WIN32_OP_FAIL();
default:
{
t_size index = (t_size)(status - (WAIT_OBJECT_0 + 1));
if (index == evCount) {
ProcessPendingMessages();
} else if (index < evCount) {
return index;
} else {
uBugCheck();
}
}
}
}
}
开发者ID:unjello,项目名称:foo_input_amr,代码行数:29,代码来源:ThreadUtils.cpp
示例5: open_path
bool input_helper::open_path(file::ptr p_filehint,const char * path,abort_callback & p_abort,bool p_from_redirect,bool p_skip_hints) {
p_abort.check();
if (!need_file_reopen(path)) return false;
m_input.release();
service_ptr_t<file> l_file = p_filehint;
process_fullbuffer(l_file,path,m_fullbuffer,p_abort);
TRACK_CODE("input_entry::g_open_for_decoding",
input_entry::g_open_for_decoding(m_input,l_file,path,p_abort,p_from_redirect)
);
if (!p_skip_hints) {
try {
static_api_ptr_t<metadb_io>()->hint_reader(m_input.get_ptr(),path,p_abort);
} catch(exception_io_data) {
//Don't fail to decode when this barfs, might be barfing when reading info from another subsong than the one we're trying to decode etc.
m_input.release();
if (l_file.is_valid()) l_file->reopen(p_abort);
TRACK_CODE("input_entry::g_open_for_decoding",
input_entry::g_open_for_decoding(m_input,l_file,path,p_abort,p_from_redirect)
);
}
}
m_path = path;
return true;
}
开发者ID:0xmono,项目名称:miranda-ng,代码行数:30,代码来源:input_helpers.cpp
示例6: open
void input_helper::open(service_ptr_t<file> p_filehint,const playable_location & p_location,unsigned p_flags,abort_callback & p_abort,bool p_from_redirect,bool p_skip_hints) {
p_abort.check();
if (m_input.is_empty() || metadb::path_compare(p_location.get_path(),m_path) != 0)
{
m_input.release();
service_ptr_t<file> l_file = p_filehint;
process_fullbuffer(l_file,p_location.get_path(),m_fullbuffer,p_abort);
TRACK_CODE("input_entry::g_open_for_decoding",
input_entry::g_open_for_decoding(m_input,l_file,p_location.get_path(),p_abort,p_from_redirect)
);
if (!p_skip_hints) {
try {
static_api_ptr_t<metadb_io>()->hint_reader(m_input.get_ptr(),p_location.get_path(),p_abort);
} catch(exception_io_data) {
//don't fail to decode when this barfs
m_input.release();
if (l_file.is_valid()) l_file->reopen(p_abort);
TRACK_CODE("input_entry::g_open_for_decoding",
input_entry::g_open_for_decoding(m_input,l_file,p_location.get_path(),p_abort,p_from_redirect)
);
}
}
m_path = p_location.get_path();
}
TRACK_CODE("input_decoder::initialize",m_input->initialize(p_location.get_subsong_index(),p_flags,p_abort));
}
开发者ID:AICIDNN,项目名称:lastfm-desktop,代码行数:33,代码来源:input_helpers.cpp
示例7: write
void stream_writer_buffered::write(const void * p_buffer,t_size p_bytes,abort_callback & p_abort) {
p_abort.check_e();
const char * source = (const char*)p_buffer;
t_size source_remaining = p_bytes;
const t_size buffer_size = m_buffer.get_size();
if (source_remaining >= buffer_size)
{
flush(p_abort);
m_base->write_object(source,source_remaining,p_abort);
return;
}
if (m_buffer_ptr + source_remaining >= buffer_size)
{
t_size delta = buffer_size - m_buffer_ptr;
memcpy(m_buffer.get_ptr() + m_buffer_ptr, source,delta);
source += delta;
source_remaining -= delta;
m_buffer_ptr += delta;
flush(p_abort);
}
memcpy(m_buffer.get_ptr() + m_buffer_ptr, source,source_remaining);
m_buffer_ptr += source_remaining;
}
开发者ID:exscape,项目名称:foo_simpleserver,代码行数:25,代码来源:stream_buffer_helper.cpp
示例8: seek
void seekabilizer::seek(t_filesize p_position,abort_callback & p_abort) {
assert(m_position_base >= m_buffer.get_depth());
p_abort.check_e();
if (m_size != filesize_invalid && p_position > m_size) throw exception_io_seek_out_of_range();
t_filesize lowest = m_position_base - m_buffer.get_depth();
if (p_position < lowest) {
if (m_file->can_seek()) {
m_buffer.reset();
t_filesize target = p_position;
t_size delta = m_buffer.get_max_depth();
if (delta > backread_on_seek) delta = backread_on_seek;
if (target > delta) target -= delta;
else target = 0;
m_file->seek(target,p_abort);
m_position_base = target;
}
else {
m_buffer.reset();
m_file->reopen(p_abort);
m_position_base = 0;
}
}
m_position = p_position;
}
开发者ID:9060,项目名称:columns_ui,代码行数:28,代码来源:seekabilizer.cpp
示例9: WaitAbortable_MsgLoop
bool WaitAbortable_MsgLoop(HANDLE ev, abort_callback & abort, DWORD timeout /*must not be INFINITE*/) {
PFC_ASSERT( timeout != INFINITE );
const DWORD entry = GetTickCount();
const HANDLE handles[2] = {ev, abort.get_abort_event()};
for(;;) {
const DWORD done = GetTickCount() - entry;
if (done >= timeout) return false;
SetLastError(0);
const DWORD status = MsgWaitForMultipleObjects(2, handles, FALSE, timeout - done, QS_ALLINPUT);
switch(status) {
case WAIT_TIMEOUT:
return false;
case WAIT_OBJECT_0:
return true;
case WAIT_OBJECT_0 + 1:
throw exception_aborted();
case WAIT_OBJECT_0 + 2:
ProcessPendingMessages();
break;
case WAIT_FAILED:
WIN32_OP_FAIL();
default:
uBugCheck();
}
}
}
开发者ID:unjello,项目名称:foo_input_amr,代码行数:26,代码来源:ThreadUtils.cpp
示例10: get_sorted_playlist
pfc::list_t<metadb_handle_ptr> get_sorted_playlist(const pfc::list_base_const_t<metadb_handle_ptr> &data, threaded_process_status &p_status, abort_callback &p_abort) {
std::multimap<int, metadb_handle_ptr, std::greater<int>> temp;
std::multimap<int, metadb_handle_ptr, std::greater<int>>::iterator it;
pfc::list_t<metadb_handle_ptr> result;
pfc::lores_timer timer;
pfc::string8 message, msg;
int size = data.get_count();
for (int i = 0; i < size; i++) {
message.reset();
message << "Track " << i + 1 << " of " << size;
p_status.set_item(message);
p_status.set_progress(i + 1, size);
timer.start();
const metadb_handle_ptr track = data[i];
int count = get_track_count(track, p_abort);
msg.reset();
msg << count;
console::print(msg);
// don't make more than 5 requests per second
// (averaged over a 5 minute period)
if (timer.query() < 0.2) {
p_abort.sleep(0.2);
}
if (i && (i % 100 == 0)) {
p_abort.sleep(10);
}
if (count > 0) {
temp.insert(std::pair<int, metadb_handle_ptr>(count, track));
} else {
temp.insert(std::pair<int, metadb_handle_ptr>(0, track));
}
}
for (it = temp.begin(); it != temp.end(); it++) {
metadb_handle_ptr track = it->second;
result.add_item(track);
}
return result;
}
开发者ID:epsil,项目名称:foo_lastsort,代码行数:47,代码来源:foo_lastsort.cpp
示例11: createFile
HANDLE createFile(LPCTSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode, LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes, HANDLE hTemplateFile, abort_callback & abort) {
abort.check();
return CreateFile(lpFileName, dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile);
// CancelSynchronousIo() doesn't fucking work. Useless.
#if 0
pCancelSynchronousIo_t pCancelSynchronousIo = (pCancelSynchronousIo_t) GetProcAddress(GetModuleHandle(TEXT("kernel32.dll")), "CancelSynchronousIo");
if (pCancelSynchronousIo == NULL) {
#ifdef _DEBUG
uDebugLog() << "Async CreateFile unavailable - using regular";
#endif
return CreateFile(lpFileName, dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile);
} else {
#ifdef _DEBUG
uDebugLog() << "Starting async CreateFile...";
pfc::hires_timer t; t.start();
#endif
createFileData_t data = {lpFileName, dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile, NULL, 0};
HANDLE hThread = (HANDLE) _beginthreadex(NULL, 0, createFileProc, &data, 0, NULL);
HANDLE waitHandles[2] = {hThread, abort.get_abort_event()};
switch(WaitForMultipleObjects(2, waitHandles, FALSE, INFINITE)) {
case WAIT_OBJECT_0: // succeeded
break;
case WAIT_OBJECT_0 + 1: // abort
#ifdef _DEBUG
uDebugLog() << "Aborting async CreateFile...";
#endif
pCancelSynchronousIo(hThread);
WaitForSingleObject(hThread, INFINITE);
break;
default:
uBugCheck();
}
CloseHandle(hThread);
SetLastError(data.dwErrorCode);
#ifdef _DEBUG
uDebugLog() << "Async CreateFile completed in " << pfc::format_time_ex(t.query(), 6) << ", status: " << (uint32_t) data.dwErrorCode;
#endif
if (abort.is_aborting()) {
if (data.hResult != INVALID_HANDLE_VALUE) CloseHandle(data.hResult);
throw exception_aborted();
}
return data.hResult;
}
#endif
}
开发者ID:Irwin1138,项目名称:foo_bestversion,代码行数:47,代码来源:file_win32_wrapper.cpp
示例12: readOverlappedPass
DWORD readOverlappedPass(HANDLE handle, HANDLE myEvent, t_filesize position, void * out, DWORD outBytes, abort_callback & abort) {
abort.check();
if (outBytes == 0) return 0;
OVERLAPPED ol = {};
fillOverlapped(ol, myEvent, position);
ResetEvent(myEvent);
DWORD bytesDone;
SetLastError(NO_ERROR);
if (ReadFile( handle, out, outBytes, &bytesDone, &ol)) {
// succeeded already?
return bytesDone;
}
{
const DWORD code = GetLastError();
switch(code) {
case ERROR_HANDLE_EOF:
case ERROR_BROKEN_PIPE:
return 0;
case ERROR_IO_PENDING:
break; // continue
default:
exception_io_from_win32(code);
};
}
const HANDLE handles[] = {myEvent, abort.get_abort_event()};
SetLastError(NO_ERROR);
DWORD state = WaitForMultipleObjects(_countof(handles), handles, FALSE, INFINITE);
if (state == WAIT_OBJECT_0) {
SetLastError(NO_ERROR);
if (!GetOverlappedResult(handle,&ol,&bytesDone,TRUE)) {
const DWORD code = GetLastError();
if (code == ERROR_HANDLE_EOF || code == ERROR_BROKEN_PIPE) bytesDone = 0;
else {
CancelIo(handle);
exception_io_from_win32(code);
}
}
return bytesDone;
}
CancelIo(handle);
throw exception_aborted();
}
开发者ID:Irwin1138,项目名称:foo_bestversion,代码行数:44,代码来源:file_win32_wrapper.cpp
示例13: read
t_size seekabilizer::read(void * p_buffer,t_size p_bytes,abort_callback & p_abort) {
p_abort.check_e();
if (m_position > m_position_base + pfc::max_t<t_size>(m_buffer.get_max_depth(),backread_on_seek) && m_file->can_seek()) {
m_buffer.reset();
t_filesize target = m_position;
if (target < backread_on_seek) target = 0;
else target -= backread_on_seek;
m_file->seek(target,p_abort);
m_position_base = target;
}
//seek ahead
while(m_position > m_position_base) {
enum {tempsize = 1024};
t_uint8 temp[tempsize];
t_size delta = (t_size) pfc::min_t<t_filesize>(tempsize,m_position - m_position_base);
t_size bytes_read = 0;
bytes_read = m_file->read(temp,delta,p_abort);
m_buffer.write(temp,bytes_read);
m_position_base += bytes_read;
if (bytes_read < delta) {
return 0;
}
}
t_size done = 0;
t_uint8 * targetptr = (t_uint8*) p_buffer;
//try to read backbuffer
if (m_position < m_position_base) {
if (m_position_base - m_position > (t_filesize)m_buffer.get_depth()) throw exception_io_seek_out_of_range();
t_size backread_depth = (t_size) (m_position_base - m_position);
t_size delta = pfc::min_t<t_size>(backread_depth,p_bytes-done);
m_buffer.read(backread_depth,targetptr,delta);
done += delta;
m_position += delta;
}
//regular read
if (done < p_bytes)
{
t_size bytes_read;
bytes_read = m_file->read(targetptr+done,p_bytes-done,p_abort);
m_buffer.write(targetptr+done,bytes_read);
done += bytes_read;
m_position += bytes_read;
m_position_base += bytes_read;
}
return done;
}
开发者ID:Irwin1138,项目名称:foo_bestversion,代码行数:55,代码来源:seekabilizer.cpp
示例14: read
t_size reader_membuffer_base::read(void * p_buffer,t_size p_bytes,abort_callback & p_abort) {
p_abort.check_e();
t_size max = get_buffer_size();
if (max < m_offset) uBugCheck();
max -= m_offset;
t_size delta = p_bytes;
if (delta > max) delta = max;
memcpy(p_buffer,(char*)get_buffer() + m_offset,delta);
m_offset += delta;
return delta;
}
开发者ID:Sektorka,项目名称:AltaCast,代码行数:11,代码来源:filesystem_helper.cpp
示例15: hMutex
mutexScope::mutexScope(HANDLE hMutex_, abort_callback & abort) : hMutex(hMutex_) {
HANDLE h[2] = {hMutex, abort.get_abort_event()};
switch( WaitForMultipleObjects(2, h, FALSE, INFINITE) ) {
case WAIT_OBJECT_0:
break; // and enter
case WAIT_OBJECT_0+1:
throw exception_aborted();
default:
uBugCheck();
}
}
开发者ID:Irwin1138,项目名称:foo_bestversion,代码行数:11,代码来源:win32_misc.cpp
示例16: g_open_for_info_write_timeout
void input_entry::g_open_for_info_write_timeout(service_ptr_t<input_info_writer> & p_instance,service_ptr_t<file> p_filehint,const char * p_path,abort_callback & p_abort,double p_timeout,bool p_from_redirect) {
pfc::lores_timer timer;
timer.start();
for(;;) {
try {
g_open_for_info_write(p_instance,p_filehint,p_path,p_abort,p_from_redirect);
break;
} catch(exception_io_sharing_violation) {
if (timer.query() > p_timeout) throw;
p_abort.sleep(0.01);
}
}
}
开发者ID:unjello,项目名称:foo_input_amr,代码行数:13,代码来源:input.cpp
示例17: on_entry
bool directory_callback_impl::on_entry(filesystem * owner,abort_callback & p_abort,const char * url,bool is_subdirectory,const t_filestats & p_stats) {
p_abort.check_e();
if (is_subdirectory) {
if (m_recur) {
try {
owner->list_directory(url,*this,p_abort);
} catch(exception_io const &) {}
}
} else {
m_data.add_item(pfc::rcnew_t<t_entry>(url,p_stats));
}
return true;
}
开发者ID:KolorKode,项目名称:Stg,代码行数:13,代码来源:filesystem.cpp
示例18: flush
void stream_reader_chunk::flush(abort_callback & p_abort) {
while(!m_eof) {
p_abort.check_e();
t_uint8 temp;
m_reader->read_lendian_t(temp,p_abort);
m_buffer_size = temp;
if (temp != sizeof(m_buffer)) m_eof = true;
m_buffer_state = 0;
if (m_buffer_size>0) {
m_reader->skip_object(m_buffer_size,p_abort);
}
}
}
开发者ID:Sektorka,项目名称:AltaCast,代码行数:13,代码来源:filesystem_helper.cpp
示例19: g_transfer
t_filesize file::g_transfer(stream_reader * p_src,stream_writer * p_dst,t_filesize p_bytes,abort_callback & p_abort) {
enum {BUFSIZE = 1024*1024*8};
pfc::array_t<t_uint8> temp;
temp.set_size((t_size)pfc::min_t<t_filesize>(BUFSIZE,p_bytes));
void* ptr = temp.get_ptr();
t_filesize done = 0;
while(done<p_bytes) {
p_abort.check_e();
t_size delta = (t_size)pfc::min_t<t_filesize>(BUFSIZE,p_bytes-done);
delta = p_src->read(ptr,delta,p_abort);
if (delta<=0) break;
p_dst->write(ptr,delta,p_abort);
done += delta;
}
return done;
}
开发者ID:KolorKode,项目名称:Stg,代码行数:16,代码来源:filesystem.cpp
示例20: index_tracks_helper
static void index_tracks_helper(const char * p_path,const service_ptr_t<file> & p_reader,const t_filestats & p_stats,playlist_loader_callback::t_entry_type p_type,playlist_loader_callback::ptr p_callback, abort_callback & p_abort,bool & p_got_input)
{
TRACK_CALL_TEXT("index_tracks_helper");
if (p_reader.is_empty() && filesystem::g_is_remote_safe(p_path))
{
TRACK_CALL_TEXT("remote");
metadb_handle_ptr handle;
p_callback->handle_create(handle,make_playable_location(p_path,0));
p_got_input = true;
p_callback->on_entry(handle,p_type,p_stats,true);
} else {
TRACK_CALL_TEXT("hintable");
service_ptr_t<input_info_reader> instance;
input_entry::g_open_for_info_read(instance,p_reader,p_path,p_abort);
t_filestats stats = instance->get_file_stats(p_abort);
t_uint32 subsong,subsong_count = instance->get_subsong_count();
bool bInfoGetError = false;
for(subsong=0;subsong<subsong_count;subsong++)
{
TRACK_CALL_TEXT("subsong-loop");
p_abort.check();
metadb_handle_ptr handle;
t_uint32 index = instance->get_subsong(subsong);
p_callback->handle_create(handle,make_playable_location(p_path,index));
p_got_input = true;
if (! bInfoGetError && p_callback->want_info(handle,p_type,stats,true) )
{
file_info_impl info;
try {
TRACK_CODE("get_info",instance->get_info(index,info,p_abort));
} catch(std::exception const & e) {
bInfoGetError = true;
}
p_callback->on_entry_info(handle,p_type,stats,info,true);
}
else
{
p_callback->on_entry(handle,p_type,stats,true);
}
}
}
}
开发者ID:Sektorka,项目名称:AltaCast,代码行数:45,代码来源:playlist_loader.cpp
注:本文中的abort_callback类示例由纯净天空整理自Github/MSDocs等源码及文档管理平台,相关代码片段筛选自各路编程大神贡献的开源项目,源码版权归原作者所有,传播和使用请参考对应项目的License;未经允许,请勿转载。 |
请发表评论