|
Cheat Engine The Official Site of Cheat Engine
|
View previous topic :: View next topic |
Author |
Message |
Antoshick Advanced Cheater Reputation: 0
Joined: 02 Nov 2018 Posts: 56
|
Posted: Mon Nov 22, 2021 11:41 am Post subject: WinAPI - Static lable controls slightly flicker |
|
|
Hi, on c++ project what can i do for avoid flickering of transparent static text control when parent window is updated very fast?
Parent window has style - WS_CLIPCHILDREN.
Static text control has Exstyle - WS_EX_TRANSPARENT,
case WM_CTLCOLORSTATIC: set BkMode to TRANSPARENT and return HOLLOW_BRUSH.
So label work and appears fine. Problem only is flickering if i upatete parent window very fast.
In CE i tested same, but there is no label's flickring, there is all fine.
WHere i need to look to fix that?
Hi, in a c++ project, what can I do to avoid a transparent static text control flickering when the parent window is updated very quickly?
The parent window has the style - WS_CLIPCHILDREN.
The static text control has Exstyle - WS_EX_TRANSPARENT,
case WM_CTLCOLORSTATIC: set the BkMode to TRANSPARENT and return the HOLLOW_BRUSH.
So the label works and looks fine. The problem is only flickering if I refresh the parent window very quickly.
In CE i tested the same thing, but there is no label flicker, everything is fine.
Where do I need to look to fix this?
|
|
Back to top |
|
|
LeFiXER Grandmaster Cheater Supreme Reputation: 20
Joined: 02 Sep 2011 Posts: 1055 Location: 0x90
|
Posted: Mon Nov 22, 2021 5:46 pm Post subject: |
|
|
Look up double buffering.
|
|
Back to top |
|
|
Antoshick Advanced Cheater Reputation: 0
Joined: 02 Nov 2018 Posts: 56
|
Posted: Mon Nov 22, 2021 10:28 pm Post subject: |
|
|
LeFiXER wrote: | Look up double buffering. |
for picture control i used now not static image control, but simple window, with painting bitmap on it with using double buffering, and now there is no flickering with it picture. But for static text control i can't use the same method. Especially because backcolor of glifs must be transparent.
I tryed also another way for create static text on form, - i create simple window, as child, with using WS_EX_TRANSPARENT style. So now, in this situation, as i understand, this window receive paint message only when elements underneath are already was repainted. And i in WM_PAINT of this window uses double buffering with this code
Code: | case WM_ERASEBKGND:
return (LRESULT)1;
case WM_PAINT:
BeginPaint(hWnd, &ps);
m_hCompatibleBitmap = CreateCompatibleBitmap(hDC, m_Width, m_Height);
m_hOldCompatibleBitmap = static_cast<HBITMAP>(SelectObject(m_CompatibleDC, m_hCompatibleBitmap));
BitBlt(m_CompatibleDC, 0, 0, m_Width, m_Height, hDC, 0, 0, SRCCOPY);
TextOutW(m_CompatibleDC, 0, 0, L"SomemeDDDDDDDeee", 17);
BitBlt(hDC, 0, 0, m_Width, m_Height, m_CompatibleDC, 0, 0, SRCCOPY); //hDC - is static DC which i get early, because i use CS_OWNDC window class style
DeleteObject(SelectObject(m_CompatibleDC, m_hOldCompatibleBitmap));
EndPaint(hWnd, &ps);
break; |
But stiil with this method statix text is flickering.
|
|
Back to top |
|
|
Antoshick Advanced Cheater Reputation: 0
Joined: 02 Nov 2018 Posts: 56
|
Posted: Tue Nov 23, 2021 8:27 am Post subject: |
|
|
Example of the flickering control code. If you quickly move the scroll slider up and down, you will see how the text control flashes
Code: | #pragma comment(linker,"\"/manifestdependency:type='win32' \
name='Microsoft.Windows.Common-Controls' version='6.0.0.0' \
processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
#include <windows.h>
#include <windowsx.h>
#include <string>
#include <stdio.h>
#include "commctrl.h"
#pragma comment(lib, "comctl32")
HWND hMainWnd { NULL };
HWND hStaticTextCtrl{ NULL };
HBRUSH hHollowBrush{ NULL };
SCROLLINFO scrollInfo{ 0 };
int scrollInc = 10;
LRESULT CALLBACK WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
void Scrolling(int yInc);
int APIENTRY wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPWSTR lpCmdLine, _In_ int nCmdShow)
{
MSG msg;
WNDCLASSEXW m_WndClass{ 0 };
m_WndClass.cbSize = sizeof(m_WndClass);
m_WndClass.style = CS_HREDRAW | CS_VREDRAW;
m_WndClass.lpfnWndProc = WndProc;
m_WndClass.cbClsExtra = 0;
m_WndClass.cbWndExtra = 0;
m_WndClass.hInstance = hInstance;
m_WndClass.hIcon = LoadIconW(NULL, IDI_APPLICATION);
m_WndClass.hCursor = LoadCursorW(NULL, IDC_ARROW);
m_WndClass.hbrBackground = CreateSolidBrush(RGB(200, 230, 200));
m_WndClass.lpszMenuName = NULL;
m_WndClass.lpszClassName = L"MainWndClass";
m_WndClass.hIconSm = LoadIconW(NULL, IDI_APPLICATION);
RegisterClassExW(&m_WndClass);
hMainWnd = CreateWindowExW(0, L"MainWndClass", L"MainWindow",
WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN,
100, 100, 400, 400, NULL, NULL, hInstance, NULL);
ShowWindow(hMainWnd, SW_SHOW);
hStaticTextCtrl = CreateWindowExW(WS_EX_TRANSPARENT, L"Static", L"SomeTextThere",
WS_VISIBLE | WS_CHILD | SS_LEFT | WS_CLIPSIBLINGS,
30, 130, 100, 20, hMainWnd, NULL, hInstance, NULL);
ShowWindow(hMainWnd, SW_SHOW);
hHollowBrush = reinterpret_cast<HBRUSH>(GetStockObject(HOLLOW_BRUSH));
while (GetMessageW(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessageW(&msg);
}
return 0;
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_CREATE:
scrollInfo.cbSize = sizeof(SCROLLINFO);
scrollInfo.fMask = SIF_RANGE | SIF_PAGE | SIF_POS;
scrollInfo.nMax = 100;
scrollInfo.nPage = 10;
ShowScrollBar(hWnd, SB_VERT, TRUE);
SetScrollInfo(hWnd, SB_VERT, &scrollInfo, TRUE);
break;
case WM_CTLCOLORSTATIC:
SetBkMode(reinterpret_cast<HDC>(wParam), TRANSPARENT);
SetTextColor(reinterpret_cast<HDC>(wParam), RGB(200, 50, 100));
return reinterpret_cast<INT_PTR>(hHollowBrush);
//case WM_ERASEBKGND:
// return TRUE;
case WM_PAINT:
{
PAINTSTRUCT ps{ 0 };
HDC hDC = NULL;
hDC = BeginPaint(hWnd, &ps);
SetBkMode(hDC, TRANSPARENT);
TextOutW(hDC, 100, 100, L"This is a Main Window", wcslen(L"This is a Main Window"));
EndPaint(hWnd, &ps);
}
break;
case WM_LBUTTONUP: //move the static text control down by 7 pixels when clicking on the client area of the main window
{
RECT rect{ 0 };
POINT point{ 0 };
GetWindowRect(hStaticTextCtrl, &rect);
point.x = rect.left;
point.y = rect.top;
ScreenToClient(hMainWnd, &point);
point.y += 7;
SetWindowPos(hStaticTextCtrl, NULL, point.x, point.y, 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED | SWP_NOACTIVATE);
RedrawWindow(hMainWnd, NULL, NULL, RDW_UPDATENOW | RDW_ERASENOW | RDW_ERASE | RDW_INVALIDATE | RDW_INTERNALPAINT | RDW_ALLCHILDREN);
}
break;
case WM_VSCROLL:
if (!lParam) //the message is from the standard scrollbar, not from the scrollbar control.
{
switch (LOWORD(wParam))
{
case SB_LINEUP:
Scrolling(-scrollInc); break;
case SB_LINEDOWN:
Scrolling(scrollInc); break;
case SB_PAGEUP:
Scrolling(-static_cast<int>(scrollInfo.nPage)); break;
case SB_PAGEDOWN:
Scrolling(static_cast<int>(scrollInfo.nPage)); break;
case SB_THUMBTRACK:
Scrolling(HIWORD(wParam) - scrollInfo.nPos); break;
case SB_TOP:
Scrolling(-scrollInfo.nMax); break;
case SB_BOTTOM:
Scrolling(scrollInfo.nMax); break;
default: Scrolling(0);
}
}
break;
case WM_MOUSEWHEEL:
Scrolling(-scrollInc * static_cast<short>(HIWORD(wParam)) / WHEEL_DELTA);
break;
case WM_CLOSE:
DestroyWindow(hWnd);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProcW(hWnd, uMsg, wParam, lParam);
}
return 0;
}
void Scrolling(int yInc)
{
if (scrollInfo.nPos + yInc < 0)
{
yInc = scrollInfo.nPos;
scrollInfo.nPos = 0;
::ScrollWindow(hMainWnd, 0, yInc, NULL, NULL);
SetScrollInfo(hMainWnd, SB_VERT, &scrollInfo, TRUE);
}
else if (scrollInfo.nPos + yInc > scrollInfo.nMax - static_cast<int>(scrollInfo.nPage))
{
yInc = (scrollInfo.nMax - static_cast<int>(scrollInfo.nPage)) - scrollInfo.nPos;
scrollInfo.nPos = scrollInfo.nMax - static_cast<int>(scrollInfo.nPage);
::ScrollWindow(hMainWnd, 0, -yInc, NULL, NULL);
SetScrollInfo(hMainWnd, SB_VERT, &scrollInfo, TRUE);
}
else
{
scrollInfo.nPos += yInc;
::ScrollWindow(hMainWnd, 0, -yInc, NULL, NULL);
SetScrollInfo(hMainWnd, SB_VERT, &scrollInfo, TRUE);
}
RedrawWindow(hMainWnd, NULL, NULL, RDW_UPDATENOW | RDW_ERASENOW | RDW_ERASE | RDW_INVALIDATE | RDW_INTERNALPAINT | RDW_ALLCHILDREN);
//RedrawWindow(hMainWnd, NULL, NULL, RDW_INVALIDATE | RDW_ALLCHILDREN);
//InvalidateRect(m_Owner.GetHWND(), NULL, TRUE);
//RedrawWindow(m_Owner.GetHWND(), NULL, NULL, RDW_ERASE | RDW_INVALIDATE | RDW_INTERNALPAINT | RDW_ALLCHILDREN);
//RedrawWindow(m_Owner.GetHWND(), NULL, NULL, RDW_INVALIDATE | RDW_ALLCHILDREN);
//RedrawWindow(m_Owner.GetHWND(), NULL, NULL, RDW_ERASE | RDW_INVALIDATE | RDW_INTERNALPAINT );
//SetWindowPos(m_Owner.GetHWND(), NULL, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED | SWP_NOACTIVATE);
//InvalidateRect(hMainWnd, NULL, TRUE);
//InvalidateRect(hStaticTextCtrl, NULL, TRUE);
//UpdateWindow(hMainWnd);
//UpdateWindow(hStaticTextCtrl);
} |
|
|
Back to top |
|
|
thirdfoot Newbie cheater Reputation: 0
Joined: 09 Feb 2019 Posts: 21
|
Posted: Wed Nov 24, 2021 2:14 pm Post subject: |
|
|
It's the nature of the message pumping beast, it's quirky and better to just roll your own realtime loop. If you want to have static painted stuff unaffected by WM_PAINT and any kind of flickering on a window you also use normal controls on, you can overlay a window on the existing one that uses GDI double buffering and full transparency (including the interactions to the window below). It will resize and move itself via WM_MOVING and WM_SIZING. May have forgotten the last but just copy the code.
I made some modifications to your code. Quick and dirty but tested OK.
Code: | #include <windows.h>
#include <windowsx.h>
#include <string>
#include <stdio.h>
#include "commctrl.h"
#pragma comment(lib, "comctl32")
HWND hMainWnd { NULL };
HWND hStaticTextCtrl{ NULL };
HBRUSH hHollowBrush{ NULL };
SCROLLINFO scrollInfo{ 0 };
int scrollInc = 10;
LRESULT CALLBACK WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
void Scrolling(int yInc);
//
// Transparent layer stuff.
//
HWND LayerWnd;
RECT LayerRect, hMainWndRect;
HBITMAP BufferBitmap;
HDC LayerDC, BufferDC;
COLORREF TrueWhite = RGB(255,255,255);
HBRUSH BgBrush;
//
// Layered window paint function.
// Call this from the realtime loop in wWinMain.
//
void PaintLayerStuff()
{
// Prep DCs for buffering.
LayerDC = GetDC(LayerWnd);
BufferDC = CreateCompatibleDC(LayerDC);
GetClientRect(hMainWnd, &LayerRect);
BufferBitmap = CreateCompatibleBitmap(LayerDC, LayerRect.right, LayerRect.bottom);
SelectObject(BufferDC, BufferBitmap);
// Overwrite prev background to avoid artifacts.
BgBrush = CreateSolidBrush(TrueWhite);
FillRect(BufferDC, &LayerRect, BgBrush);
DeleteObject(BgBrush);
// Draw your text.
TextOutW(BufferDC, 100, 100, L"This is a Main Window", wcslen(L"This is a Main Window"));
// Flip the buffer to the window DC and clean up.
BitBlt(LayerDC, 0, 0, LayerRect.right, LayerRect.bottom, BufferDC, 0, 0, SRCCOPY);
DeleteObject(BufferBitmap);
DeleteDC(BufferDC);
ReleaseDC(hMainWnd, LayerDC);
}
int APIENTRY wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPWSTR lpCmdLine, _In_ int nCmdShow)
{
MSG msg;
WNDCLASSEXW m_WndClass{ 0 };
m_WndClass.cbSize = sizeof(m_WndClass);
m_WndClass.style = CS_HREDRAW | CS_VREDRAW;
m_WndClass.lpfnWndProc = WndProc;
m_WndClass.cbClsExtra = 0;
m_WndClass.cbWndExtra = 0;
m_WndClass.hInstance = hInstance;
m_WndClass.hIcon = LoadIconW(NULL, IDI_APPLICATION);
m_WndClass.hCursor = LoadCursorW(NULL, IDC_ARROW);
m_WndClass.hbrBackground = CreateSolidBrush(RGB(200, 230, 200));
m_WndClass.lpszMenuName = NULL;
m_WndClass.lpszClassName = L"MainWndClass";
m_WndClass.hIconSm = LoadIconW(NULL, IDI_APPLICATION);
RegisterClassExW(&m_WndClass);
hMainWnd = CreateWindowExW(0, L"MainWndClass", L"MainWindow",
WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN,
100, 100, 400, 400, NULL, NULL, hInstance, NULL);
ShowWindow(hMainWnd, SW_SHOW);
hStaticTextCtrl = CreateWindowExW(WS_EX_TRANSPARENT, L"Static", L"SomeTextThere",
WS_VISIBLE | WS_CHILD | SS_LEFT | WS_CLIPSIBLINGS,
30, 130, 100, 20, hMainWnd, NULL, hInstance, NULL);
ShowWindow(hMainWnd, SW_SHOW);
hHollowBrush = reinterpret_cast<HBRUSH>(GetStockObject(HOLLOW_BRUSH));
//
// Layer window.
//
WNDCLASSEXW lc;
lc.cbSize = sizeof(WNDCLASSEX);
lc.style = 0;
lc.lpfnWndProc = (WNDPROC)WndProc;
lc.cbClsExtra = 0;
lc.cbWndExtra = 0;
lc.hInstance = hInstance;
lc.hIcon = LoadIconW(NULL, IDI_APPLICATION);
lc.hIconSm = LoadIconW(NULL, IDI_APPLICATION);
lc.hCursor = LoadCursor(NULL, IDC_ARROW);
lc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
lc.lpszMenuName = NULL;
lc.lpszClassName = L"LayerWindowClass";
RegisterClassExW(&lc);
// WS_EX_LAYERED is required by SetLayeredWindowAttributes.
// WS_EX_TRANSPARENT lets you interact with the window behind.
LayerWnd = CreateWindowEx(
WS_EX_LAYERED | WS_EX_TRANSPARENT,
L"LayerWindowClass",
L"",
WS_POPUP,
0,0,1,1,
NULL,
NULL,
hInstance,
NULL
);
SetLayeredWindowAttributes(LayerWnd, RGB(255, 255, 255), NULL, LWA_COLORKEY);
ShowScrollBar(LayerWnd, SB_VERT, false);
// Update layer window positioning.
GetWindowRect(hMainWnd, &hMainWndRect);
SetWindowPos(
LayerWnd,
HWND_TOPMOST,
hMainWndRect.left,
hMainWndRect.top,
hMainWndRect.right - hMainWndRect.left,
hMainWndRect.bottom - hMainWndRect.top,
SWP_SHOWWINDOW
);
//
// Realtime loop, do your realtime stuff here.
//
do {
// Paint stuff.
PaintLayerStuff();
// Only peek, don't hang around waiting.
if (PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE) != 0) {
TranslateMessage(&msg);
DispatchMessageW(&msg);
}
// cpu friendly
Sleep(10);
} while (msg.message != WM_QUIT);
return 0;
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_CREATE:
scrollInfo.cbSize = sizeof(SCROLLINFO);
scrollInfo.fMask = SIF_RANGE | SIF_PAGE | SIF_POS;
scrollInfo.nMax = 100;
scrollInfo.nPage = 10;
ShowScrollBar(hWnd, SB_VERT, TRUE);
SetScrollInfo(hWnd, SB_VERT, &scrollInfo, TRUE);
break;
case WM_CTLCOLORSTATIC:
SetBkMode(reinterpret_cast<HDC>(wParam), TRANSPARENT);
SetTextColor(reinterpret_cast<HDC>(wParam), RGB(200, 50, 100));
return reinterpret_cast<INT_PTR>(hHollowBrush);
break;
//case WM_ERASEBKGND:
// return TRUE;
/* case WM_PAINT:
{
PAINTSTRUCT ps{ 0 };
HDC hDC = NULL;
hDC = BeginPaint(hWnd, &ps);
SetBkMode(hDC, TRANSPARENT);
TextOutW(hDC, 100, 100, L"This is a Main Window", wcslen(L"This is a Main Window"));
PaintText();
EndPaint(hWnd, &ps);
}
break; */
case WM_MOVING:
GetWindowRect(hMainWnd, &hMainWndRect);
SetWindowPos(
LayerWnd,
HWND_TOP,
hMainWndRect.left,
hMainWndRect.top,
hMainWndRect.right,
hMainWndRect.bottom,
SWP_SHOWWINDOW
);
PaintLayerStuff();
break;
case WM_LBUTTONUP: //move the static text control down by 7 pixels when clicking on the client area of the main window
{
RECT rect{ 0 };
POINT point{ 0 };
GetWindowRect(hStaticTextCtrl, &rect);
point.x = rect.left;
point.y = rect.top;
ScreenToClient(hMainWnd, &point);
point.y += 7;
SetWindowPos(hStaticTextCtrl, NULL, point.x, point.y, 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED | SWP_NOACTIVATE);
RedrawWindow(hMainWnd, NULL, NULL, RDW_UPDATENOW | RDW_ERASENOW | RDW_ERASE | RDW_INVALIDATE | RDW_INTERNALPAINT | RDW_ALLCHILDREN);
}
break;
case WM_VSCROLL:
if (!lParam) //the message is from the standard scrollbar, not from the scrollbar control.
{
switch (LOWORD(wParam))
{
case SB_LINEUP:
Scrolling(-scrollInc); break;
case SB_LINEDOWN:
Scrolling(scrollInc); break;
case SB_PAGEUP:
Scrolling(-static_cast<int>(scrollInfo.nPage)); break;
case SB_PAGEDOWN:
Scrolling(static_cast<int>(scrollInfo.nPage)); break;
case SB_THUMBTRACK:
Scrolling(HIWORD(wParam) - scrollInfo.nPos); break;
case SB_TOP:
Scrolling(-scrollInfo.nMax); break;
case SB_BOTTOM:
Scrolling(scrollInfo.nMax); break;
default: Scrolling(0);
}
}
break;
case WM_MOUSEWHEEL:
Scrolling(-scrollInc * static_cast<short>(HIWORD(wParam)) / WHEEL_DELTA);
break;
case WM_CLOSE:
DestroyWindow(hWnd);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProcW(hWnd, uMsg, wParam, lParam);
}
return 0;
}
void Scrolling(int yInc)
{
if (scrollInfo.nPos + yInc < 0)
{
yInc = scrollInfo.nPos;
scrollInfo.nPos = 0;
::ScrollWindow(hMainWnd, 0, yInc, NULL, NULL);
SetScrollInfo(hMainWnd, SB_VERT, &scrollInfo, TRUE);
}
else if (scrollInfo.nPos + yInc > scrollInfo.nMax - static_cast<int>(scrollInfo.nPage))
{
yInc = (scrollInfo.nMax - static_cast<int>(scrollInfo.nPage)) - scrollInfo.nPos;
scrollInfo.nPos = scrollInfo.nMax - static_cast<int>(scrollInfo.nPage);
::ScrollWindow(hMainWnd, 0, -yInc, NULL, NULL);
SetScrollInfo(hMainWnd, SB_VERT, &scrollInfo, TRUE);
}
else
{
scrollInfo.nPos += yInc;
::ScrollWindow(hMainWnd, 0, -yInc, NULL, NULL);
SetScrollInfo(hMainWnd, SB_VERT, &scrollInfo, TRUE);
}
RedrawWindow(hMainWnd, NULL, NULL, RDW_UPDATENOW | RDW_ERASENOW | RDW_ERASE | RDW_INVALIDATE | RDW_INTERNALPAINT | RDW_ALLCHILDREN);
//RedrawWindow(hMainWnd, NULL, NULL, RDW_INVALIDATE | RDW_ALLCHILDREN);
//InvalidateRect(m_Owner.GetHWND(), NULL, TRUE);
//RedrawWindow(m_Owner.GetHWND(), NULL, NULL, RDW_ERASE | RDW_INVALIDATE | RDW_INTERNALPAINT | RDW_ALLCHILDREN);
//RedrawWindow(m_Owner.GetHWND(), NULL, NULL, RDW_INVALIDATE | RDW_ALLCHILDREN);
//RedrawWindow(m_Owner.GetHWND(), NULL, NULL, RDW_ERASE | RDW_INVALIDATE | RDW_INTERNALPAINT );
//SetWindowPos(m_Owner.GetHWND(), NULL, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED | SWP_NOACTIVATE);
//InvalidateRect(hMainWnd, NULL, TRUE);
//InvalidateRect(hStaticTextCtrl, NULL, TRUE);
//UpdateWindow(hMainWnd);
//UpdateWindow(hStaticTextCtrl);
} |
|
|
Back to top |
|
|
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum You cannot attach files in this forum You can download files in this forum
|
|