windows/.agent/skills/review-case/SKILL.md
Code review for API examples. Ensures examples follow project conventions, handle lifecycle correctly, manage threads safely, and use APIs properly.
npx skillsauth add agoraio/api-examples review-caseInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Use this skill when you need to:
Check:
InitializeAgoraEngine() or similarRtcEngineContextleaveChannel() is called before release()release() is called in PostNcDestroy() or cleanup methodCorrect Pattern:
BOOL CExampleDlg::OnInitDialog() {
CDialogEx::OnInitDialog();
InitializeAgoraEngine(); // Create once
return TRUE;
}
void CExampleDlg::PostNcDestroy() {
LeaveChannel();
if (m_rtcEngine) {
m_rtcEngine->release();
m_rtcEngine = nullptr;
}
CDialogEx::PostNcDestroy();
delete this;
}
void CExampleDlg::JoinChannel() {
if (!m_rtcEngine) return;
m_rtcEngine->joinChannel(token, channelName, "", 0);
}
void CExampleDlg::LeaveChannel() {
if (!m_rtcEngine) return;
m_rtcEngine->leaveChannel();
}
Incorrect Pattern:
See references/incorrect-lifecycle.cpp for common mistakes.
Check:
PostMessage()Correct Pattern:
// Event handler (may be called from background thread)
void CExampleRtcEngineEventHandler::onJoinChannelSuccess(const char* channel, uid_t uid, int elapsed) {
if (m_hMsgHandler) {
// Post message to main thread
::PostMessage(m_hMsgHandler, WM_MSGID(EID_JOIN_CHANNEL_SUCCESS), (WPARAM)uid, 0);
}
}
// Message handler (runs on main thread)
LRESULT CExampleDlg::OnMsgEngineEvent(WPARAM wParam, LPARAM lParam) {
// Safe to update UI here
m_statusText.SetWindowText(_T("Joined channel"));
return 0;
}
Incorrect Pattern:
See references/incorrect-thread-safety.cpp for common mistakes.
Check:
enableAudio()enableVideo()Correct Pattern:
void CExampleDlg::InitializeAgoraEngine() {
m_rtcEngine = createAgoraRtcEngine();
if (!m_rtcEngine) return;
RtcEngineContext context;
context.appId = CConfig::GetAppId();
context.eventHandler = &m_eventHandler;
m_eventHandler.SetMsgReceiver(m_hWnd);
m_rtcEngine->initialize(context);
// Check device availability
if (m_rtcEngine->enableVideo() == 0) {
// Video enabled successfully
}
if (m_rtcEngine->enableAudio() == 0) {
// Audio enabled successfully
}
}
Check:
joinChannel() return value checkedonError() callback implementedCorrect Pattern:
void CExampleDlg::JoinChannel() {
if (!m_rtcEngine) return;
const char* token = CConfig::GetToken("test");
int ret = m_rtcEngine->joinChannel(token, "test", "", 0);
if (ret != 0) {
// Handle error
MessageBox(_T("Failed to join channel"), _T("Error"));
}
}
void CExampleRtcEngineEventHandler::onError(int err) {
if (m_hMsgHandler) {
::PostMessage(m_hMsgHandler, WM_MSGID(EID_ERROR), (WPARAM)err, 0);
}
}
LRESULT CExampleDlg::OnMsgEngineEvent(WPARAM wParam, LPARAM lParam) {
if (wParam == EID_ERROR) {
int errorCode = (int)lParam;
// Handle error
}
return 0;
}
Check:
C<ExampleName>DlgC<ExampleName>RtcEngineEventHandlerm_ prefixCorrect Pattern:
// Header: CScreenShareDlg.h
class CScreenShareRtcEngineEventHandler : public IRtcEngineEventHandler {
// ...
};
class CScreenShareDlg : public CDialogEx {
DECLARE_DYNAMIC(CScreenShareDlg)
private:
IRtcEngine* m_rtcEngine = nullptr;
CScreenShareRtcEngineEventHandler m_eventHandler;
uid_t m_remoteUid = 0;
bool m_isJoined = false;
BEGIN_MESSAGE_MAP(CScreenShareDlg, CDialogEx)
ON_BN_CLICKED(IDC_BUTTON_JOIN, &CScreenShareDlg::OnBnClickedButtonJoin)
END_MESSAGE_MAP()
};
Check:
Correct Pattern:
// Correct order: create -> initialize -> enable -> join
m_rtcEngine = createAgoraRtcEngine();
m_rtcEngine->initialize(context);
m_rtcEngine->enableVideo();
m_rtcEngine->enableAudio();
m_rtcEngine->joinChannel(token, channelName, "", 0);
Incorrect Pattern:
// ❌ Wrong order
m_rtcEngine->joinChannel(...); // Join first
m_rtcEngine->enableVideo(); // Enable after join (too late)
Check:
Correct Pattern:
void CExampleDlg::LeaveChannel() {
if (!m_rtcEngine) return;
m_rtcEngine->stopAudioMixing(); // Stop audio
m_rtcEngine->stopScreenCapture(); // Stop screen share
m_rtcEngine->leaveChannel();
m_isJoined = false;
}
void CExampleDlg::PostNcDestroy() {
LeaveChannel();
if (m_rtcEngine) {
m_rtcEngine->release();
m_rtcEngine = nullptr;
}
CDialogEx::PostNcDestroy();
delete this;
}
When reviewing, provide feedback in this format:
## Review Results
### ✅ Passed
- Engine lifecycle correctly managed
- Thread safety ensured with message map pattern
- Error handling implemented for joinChannel
### ⚠️ Issues Found
**[HIGH] Thread Safety Issue**
- File: `CScreenShareDlg.cpp`
- Line: 45
- Issue: Direct UI update in event handler without PostMessage
- Suggestion: Use PostMessage to post event to main thread
**[MEDIUM] Missing Error Handling**
- File: `CScreenShareDlg.cpp`
- Line: 78
- Issue: joinChannel() return value not checked
- Suggestion: Check return value and handle errors
### 🔧 Recommendations
- Add logging for debugging
- Consider adding retry logic for network failures
Check:
Correct Pattern:
// Windows: Use MFC
#include "stdafx.h"
#include "APIExample.h"
class CExampleDlg : public CDialogEx {
DECLARE_DYNAMIC(CExampleDlg)
BEGIN_MESSAGE_MAP(CExampleDlg, CDialogEx)
ON_BN_CLICKED(IDC_BUTTON_JOIN, &CExampleDlg::OnBnClickedButtonJoin)
END_MESSAGE_MAP()
};
Incorrect Pattern:
// ❌ Non-MFC patterns
using namespace std; // Avoid in MFC
auto ptr = std::make_unique<IRtcEngine>(); // Modern C++ not typical in MFC
Do NOT accept:
leaveChannel() before release()APIExample/APIExample/[Basic|Advanced]/ structureUse this checklist when reviewing an example:
Lifecycle:
leaveChannel() called before release()release() called in PostNcDestroy()Thread Safety:
PostMessage()Permissions:
Error Handling:
Code Quality:
API Usage:
Resources:
Platform:
Cause: release() called without leaveChannel() first
Fix: Always call leaveChannel() before release()
Cause: Direct UI update from event handler Fix: Use PostMessage to post event to main thread
Cause: release() not called or engine recreated
Fix: Ensure release() in PostNcDestroy() and create engine once
Cause: No token refresh handling Fix: Implement token refresh in error handler
Cause: Device not available or not enabled
Fix: Check return values of enableAudio() / enableVideo()
APIExample/APIExample/Basic/JoinChannelVideoByToken/ for referencedevelopment
Add a new API example or modify an existing one. Covers both creation and modification scenarios, including dialog class structure, message map registration, and ARCHITECTURE.md updates.
development
Add a new API example or modify an existing one. Covers both creation and modification scenarios, including file structure, registration, and ARCHITECTURE.md updates.
development
Code review for API examples. Ensures examples follow project conventions, handle lifecycle correctly, manage threads safely, and use APIs properly.
development
Guide for implementing video call functionality in business scenarios, including SDK initialization, joining channels, video encoding configuration, and event handling