From 0fbb066ff52f0c32a09cf1ac422c6f9d1e021972 Mon Sep 17 00:00:00 2001 From: Ravish Roshan Date: Mon, 9 Jun 2025 18:11:31 +0530 Subject: [PATCH 01/20] Added support for Share Target and added ReadMe --- .../WinMain.cpp | 182 +++++++++++++++++- .../AppxManifest.xml | 19 +- .../resources.pri | Bin 0 -> 6248 bytes .../cppwinrt/README.md | 39 ++++ 4 files changed, 235 insertions(+), 5 deletions(-) create mode 100644 Samples/PackageWithExternalLocation/cppwinrt/PackageWithExternalLocationCppSample/resources.pri create mode 100644 Samples/PackageWithExternalLocation/cppwinrt/README.md diff --git a/Samples/PackageWithExternalLocation/cppwinrt/PackageWithExternalLocationCppApp/WinMain.cpp b/Samples/PackageWithExternalLocation/cppwinrt/PackageWithExternalLocationCppApp/WinMain.cpp index 9815300..37e154b 100644 --- a/Samples/PackageWithExternalLocation/cppwinrt/PackageWithExternalLocationCppApp/WinMain.cpp +++ b/Samples/PackageWithExternalLocation/cppwinrt/PackageWithExternalLocationCppApp/WinMain.cpp @@ -2,16 +2,29 @@ #include #include +#include +#include +#include +#include +#include +#include +#include +#include // Add this include for GetCurrentPackageFullName +#include using namespace winrt; using namespace winrt::Windows::Management::Deployment; using namespace winrt::Windows::ApplicationModel::Activation; +using namespace winrt::Windows::ApplicationModel::DataTransfer::ShareTarget; +using namespace winrt::Windows::ApplicationModel::DataTransfer; +using namespace winrt::Windows::Storage; #pragma comment(lib, "mfplat.lib") #pragma comment(lib, "mf.lib") #pragma comment(lib, "mfreadwrite.lib") #pragma comment(lib, "evr.lib") #pragma comment(lib, "d3d9.lib") +#pragma comment(lib, "Shell32.lib") using namespace Microsoft::WRL; @@ -164,6 +177,41 @@ LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) return DefWindowProc(hwnd, msg, wParam, lParam); } +// Checks if the OS version is Windows 10 2004 (build 19041) or later +bool IsSparsePackageSupported() +{ + // Windows 10 2004 is version 10.0.19041 + OSVERSIONINFOEXW osvi = {}; + osvi.dwOSVersionInfoSize = sizeof(osvi); + + // Get the actual version using RtlGetVersion (undocumented but reliable) + typedef LONG (WINAPI* RtlGetVersionPtr)(PRTL_OSVERSIONINFOEXW); + HMODULE hMod = ::GetModuleHandleW(L"ntdll.dll"); + if (hMod) { + RtlGetVersionPtr fxPtr = (RtlGetVersionPtr)::GetProcAddress(hMod, "RtlGetVersion"); + if (fxPtr != nullptr) { + fxPtr((PRTL_OSVERSIONINFOEXW)&osvi); + wchar_t log[256]; + swprintf_s(log, L"Current OS Version: %u.%u.%u\n", osvi.dwMajorVersion, osvi.dwMinorVersion, osvi.dwBuildNumber); + OutputDebugStringW(log); + } + } + + // Log the required version + OutputDebugStringW(L"Required minimum version: 10.0.19041\n"); + + // Compare with required version + if (osvi.dwMajorVersion > 10 || + (osvi.dwMajorVersion == 10 && osvi.dwMinorVersion > 0) || + (osvi.dwMajorVersion == 10 && osvi.dwMinorVersion == 0 && osvi.dwBuildNumber >= 19041)) + { + OutputDebugStringW(L"Sparse package is supported on this OS.\n"); + return true; + } + OutputDebugStringW(L"Sparse package is NOT supported on this OS.\n"); + return false; +} + HRESULT RegisterPackageWithExternalLocation(const std::wstring& externalLocation, const std::wstring& packagePath) { winrt::Windows::Management::Deployment::PackageManager packageManager; @@ -174,7 +222,88 @@ HRESULT RegisterPackageWithExternalLocation(const std::wstring& externalLocation return static_cast(result.ExtendedErrorCode().value); } -int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE, LPSTR, int nCmdShow) +// Returns true if the app is running with package identity +bool IsRunningWithIdentity() +{ + UINT32 length = 0; + LONG rc = GetCurrentPackageFullName(&length, nullptr); + + // Existing code remains unchanged + if (rc == ERROR_INSUFFICIENT_BUFFER) + { + std::wstring packageFullName(length, L'\0'); + rc = GetCurrentPackageFullName(&length, packageFullName.data()); + return rc == ERROR_SUCCESS; + } + return false; +} + +// Relaunches the current executable +void RelaunchApplication() +{ + wchar_t exePath[MAX_PATH] = {0}; + // Ensure the buffer is zero-initialized and check for errors + DWORD len = GetModuleFileNameW(nullptr, exePath, MAX_PATH); + if (len == 0 || len == MAX_PATH) + return; // Failed to get path or path too long + + // Use ShellExecuteW to relaunch the current executable + HINSTANCE result = ShellExecuteW(nullptr, L"open", exePath, nullptr, nullptr, SW_SHOWNORMAL); + if ((INT_PTR)result <= 32) + { + // Optionally log or handle the error here + OutputDebugStringW(L"Failed to relaunch application.\n"); + } +} + +// Add this function to handle share activation +winrt::Windows::Foundation::IAsyncAction HandleShareTarget(winrt::Windows::Foundation::IInspectable const& args) +{ + auto shareArgs = args.as(); + ShareOperation shareOperation = shareArgs.ShareOperation(); + + if (shareOperation.Data().Contains(StandardDataFormats::Bitmap())) + { + auto bitmapRef = co_await shareOperation.Data().GetBitmapAsync(); + // TODO: Convert bitmapRef to a format you can display in your Win32 window + MessageBox(nullptr, L"Received a bitmap via Share Target.", L"Share Target", MB_OK); + shareOperation.ReportCompleted(); + } + else if (shareOperation.Data().Contains(StandardDataFormats::StorageItems())) + { + winrt::Windows::Foundation::Collections::IVectorView items = co_await shareOperation.Data().GetStorageItemsAsync(); + for (auto const& item : items) + { + if (auto file = item.try_as()) + { + std::wstring msg = L"Received file: " + std::wstring(file.Name().c_str()); + MessageBox(nullptr, msg.c_str(), L"Share Target", MB_OK); + } + } + shareOperation.ReportCompleted(); + } + else + { + MessageBox(nullptr, L"No supported image format received.", L"Share Target", MB_OK); + shareOperation.ReportCompleted(); + } +} + +// Helper to get the directory of the current executable (build output path) +std::wstring GetExecutableDirectory() +{ + wchar_t exePath[MAX_PATH] = {0}; + DWORD len = GetModuleFileNameW(nullptr, exePath, MAX_PATH); + if (len == 0 || len == MAX_PATH) + return L""; + std::wstring path(exePath); + size_t pos = path.find_last_of(L"\\/"); + if (pos != std::wstring::npos) + path = path.substr(0, pos); + return path; +} + +int RunWebcamAppMainLoop(HINSTANCE hInstance, int nCmdShow) { MFStartup(MF_VERSION); @@ -195,7 +324,7 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE, LPSTR, int nCmdShow) ShowWindow(hwndVideo, nCmdShow); // Initialize webcam - if (FAILED(InitializeWebcam(hwndVideo))) + if (FAILED(InitializeWebcam(hwndVideo))) { MessageBox(nullptr, L"Failed to initialize webcam. Please check if app is running with identity. If not, turn on camera usage for desktop app in Settings and relaunch.", L"Error", MB_ICONERROR); return -1; @@ -203,7 +332,7 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE, LPSTR, int nCmdShow) // Main message loop MSG msg = {}; - while (GetMessage(&msg, nullptr, 0, 0)) + while (GetMessage(&msg, nullptr, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); @@ -212,6 +341,53 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE, LPSTR, int nCmdShow) return 0; } +int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE, LPSTR, int nCmdShow) +{ + if (!IsSparsePackageSupported()) + { + OutputDebugStringW(L"Sparse package is not supported. Application is not running with package identity.\n"); + return RunWebcamAppMainLoop(hInstance, nCmdShow); + } + + winrt::init_apartment(); + + // Check if running with identity + if (!IsRunningWithIdentity()) + { + // Given that for this installer we are not creating any installer, externalLocation is derived from the build output directory + // Replace this with Existing unpackaged App Install root location here. + std::wstring externalLocation = GetExecutableDirectory(); + + // Since your Sparse package(Package with external location) .msix file will be located in the existing unpackaged app install root location, + // use the same externalLocation to get .msix. + // For this sample to work, you would need to create .msix((Package with external location)) at same location as the build executable. + std::wstring packagePath = externalLocation + L"\\PackageWithExternalLocationCppSample_1.0.0.0_x86__8h66172c634n0.msix"; + + HRESULT hr = RegisterPackageWithExternalLocation(externalLocation, packagePath); + if (SUCCEEDED(hr)) + { + RelaunchApplication(); + return 0; + } + else + { + OutputDebugStringW(L"Application is not running with package identity.\n"); + return RunWebcamAppMainLoop(hInstance, nCmdShow); + } + } + + auto activationArgs = winrt::Windows::ApplicationModel::AppInstance::GetActivatedEventArgs(); + if (activationArgs) + { + if (activationArgs.Kind() == winrt::Windows::ApplicationModel::Activation::ActivationKind::ShareTarget) + { + HandleShareTarget(activationArgs).get(); // Wait for coroutine to finish + return 0; + } + } + return RunWebcamAppMainLoop(hInstance, nCmdShow); +} + void RemovePackageWithExternalLocation() // example of how to uninstall a package with external location { winrt::Windows::Management::Deployment::PackageManager packageManager; diff --git a/Samples/PackageWithExternalLocation/cppwinrt/PackageWithExternalLocationCppSample/AppxManifest.xml b/Samples/PackageWithExternalLocation/cppwinrt/PackageWithExternalLocationCppSample/AppxManifest.xml index 6728cb6..47ea56e 100644 --- a/Samples/PackageWithExternalLocation/cppwinrt/PackageWithExternalLocationCppSample/AppxManifest.xml +++ b/Samples/PackageWithExternalLocation/cppwinrt/PackageWithExternalLocationCppSample/AppxManifest.xml @@ -1,5 +1,5 @@ - - + + + + + + + .jpg + .jpeg + .png + .gif + + StorageItems + Bitmap + + + \ No newline at end of file diff --git a/Samples/PackageWithExternalLocation/cppwinrt/PackageWithExternalLocationCppSample/resources.pri b/Samples/PackageWithExternalLocation/cppwinrt/PackageWithExternalLocationCppSample/resources.pri new file mode 100644 index 0000000000000000000000000000000000000000..d78fac54426542bd4ae85bd04020bc8b1fd0977f GIT binary patch literal 6248 zcmcgxOK%%h6h3~9?bu12#7P6R#6mpOihwh*OH>x56_i$rP^1)fQII`~C$S~J>T#ur z1)>sv0f{9)fCWpIEVE?6f>qX8Ar?hK-GuMl$JidncOs|cT4&C^_k8#D-E+=3ajVy= zbbAe(C_?pBq5?r;M5}amhAKU20Z?=I+m%N9pz}3%`Q<38?3keDdi(Ctz|OWVkYaq@ z+poJV2lj#&46*Mnn8zQ7*tZwW<9`jY7ng|Y z2L65wu~XhWz8+%#eZ)TQ|LosfX7O33IBn6U>_8l?QG(v)4WLBui9%wKI3xkF(3679 zK(dfINDi_H$wO8ktB}`$eFJhGas%=vM3)F1(+f1C&^GoGgMJDfhdxF8CZPX9+oJa) zj7w5Y3{9za8k%u4s-00ft8j81xn2~jFp^uko*1g9hw2Cm9|IRb9f6Fo%c72;MIC9z z?|*lwesie4K2$G?x*U50b!Ih+6UT8%3nxoK(vTTQ24aEL6eJCqfn*>SdeUfRFeZ+% zd?)j0=fQrOmT-$H@Snx#1z;@P#{fsPK}CF6+f)|1EZVl}FZuS{u$f4gRJs1 zC#wm%Pl=*rCJW}RViLE_(R7by3rM@7zJof)75DFC>=4+aNUA% z;d_q z`AkAfTom^$+j72`!o?IWrsj(&9N#tL;|dp7xVXY`zLIu{Bg7-O`7I*#HKiAn&MLj4 zbWZ7v(#uNED~%j2=F2NRr*vBBC7}~)e+jie&Z0anq4vj_kK3FxrIx%{3TG*trEr{q z8JAS^aR!!cdEO*&>|a$}uKYeZ{(X(V%jv>?a9y<55MgVy4XGj?zo+-;XL^VJK>G!> z45F_FLFDZ_O?Q2xSQHVW?Srs7-#!Sm3HIU83qbT5GVX?>F#uG14aR2xMS8wL%mxr@ zo?sLQP%@rP-;ZA<-*(x|cfdZzchG(g-x{L%naH;Wn)%kK$$V>snQx7{TE5F6{vEK7 z@g20E!?%Wn@~wepzBOtx-x^`&TcfU&Z#yKP1noh-$L!;L`;aiceV~zVA2pG0A7SL% zM_no3r4YV@_8{M5_Hn*_NEqKf(8#xsn#i}0F!JrA!ujU^E$G6p%5Bm3Wb{0P?}1LU zf6(c*)|-vPdjDA)jP%`FrO#hD`(EQ4yfkmi94`L;z+98w)3176yL6d#x2Ha@U6u#F z>ho^empSkB_gmV!%({2Ncs99|@XYCWo@!m~QkPiw<7v*TUFs6+PIX@GQdhmMcBu=k zTNfz#;C#jZO(D8DiRW`NlNccWj^Mc_<)`V_8F8B_*SU3p^NhI3l9muDyZdjkJ_z`rm22Q_IsDF6Tf literal 0 HcmV?d00001 diff --git a/Samples/PackageWithExternalLocation/cppwinrt/README.md b/Samples/PackageWithExternalLocation/cppwinrt/README.md new file mode 100644 index 0000000..3912c39 --- /dev/null +++ b/Samples/PackageWithExternalLocation/cppwinrt/README.md @@ -0,0 +1,39 @@ +This repo contains a sample C++ application demonstrating how to use [Signed Sparse Packages](https://aka.ms/sparsepkgblog) to give a non-packaged desktop app access to new Windows APIs and features. + +## Instructions + +You can learn more about Signed Sparse Packages and Identity, Registration & Activation of Win32 apps in this [blogpost](https://aka.ms/sparsepkgblog) and in the [documentation](https://aka.ms/sparsepkgdocs). + +### Requirements + +1. Windows SDK version 10.0.19000.0 + +2. Windows OS version 10.0.19000.0 + +3. Microsoft Visual C++ Redistributables + +### PackageWithExternalLocationCppApp + +A non-package native Windows desktop Win32 GUI application written in C++. It installs a Signed Sparse Package and uses it to act as a Share Target. + +* Registration of a Signed Sparse Package and handling of Shared photos happens in WinMain.cpp. + +* Files to package and sign to create a Sparse Package for use with the app are located in the PackageWithExternalLocationCppSample directory. + + +### Building and running the sample + +1. Make sure your machine has Developer Mode turned on. +2. Retarget the solution to the SDK version on your machine – Right click -> Retarget solution. +3. Add a project reference to the Windows.winmd file at "C:\Program Files (x86)\Windows Kits\10\UnionMetadata\\\Windows.winmd". (Right click PhotoStoreDemo project | Add | Reference| Browse | All files | Windows.winmd) +4. Update the Publisher value in the AppxManifest.xml file and in PackageWithExternalLocationCppApp.exe.manifest to match the Publisher value in your cert. If you need to create a cert for signing have a look at [Creating an app package signing certificate](https://docs.microsoft.com/en-us/windows/win32/appxpkg/how-to-create-a-package-signing-certificate). +5. Install your cert on the machine +6. Create a Sparse Package by packaging the updated contents of PackageWithExternalLocationCppSample using [App Packager](https://docs.microsoft.com/en-us/windows/win32/appxpkg/make-appx-package--makeappx-exe-) (MakeAppx.exe) and specifying the **/nv** flag. For example: MakeAppx.exe pack /d \ /p \ mypackage.msix /nv +7. Sign the new Sparse Package. See [Signing an app package using SignTool](https://docs.microsoft.com/en-us/windows/win32/appxpkg/how-to-sign-a-package-using-signtool) or you can also use [Device Guard Signing](https://docs.microsoft.com/en-us/microsoft-store/device-guard-signing-portal). +8. In the WinMain method (in WinMain.cpp) update the value of **externalLocation** to match the output location of your VS Build binaries and the value of **packagePath** to match the path to your signed Sparse Package (.msix). Note that these values cannot be relative paths and must be complete paths. +9. Build the app +10. Copy the PhotoStoreDemoPkg\Assets folder and resources.pri file to the same location as your VS Build binaries +11. Run the app + +### Removing the package +If you need to remove the package from package manager you can run the following command in an admin command prompt: + +powershell -c “get-appxpackage -name \*PackageWithExternalLocationCppSample\* | remove-appxpackage" \ No newline at end of file From d307e23d8251a7697971098a492aa11f10008771 Mon Sep 17 00:00:00 2001 From: Ravish Roshan Date: Thu, 18 Sep 2025 22:23:40 +0530 Subject: [PATCH 02/20] Sample chat app --- .../SampleChatAppWithShare.sln | 37 + .../SampleChatAppWithShare/Resource.h | 40 + .../SampleChatAppWithShare.cpp | 1008 +++++++++++++++++ .../SampleChatAppWithShare.h | 3 + .../SampleChatAppWithShare.ico | Bin 0 -> 46227 bytes .../SampleChatAppWithShare.rc | Bin 0 -> 7124 bytes .../SampleChatAppWithShare.vcxproj | 197 ++++ .../SampleChatAppWithShare.vcxproj.filters | 49 + .../SampleChatAppWithShare/framework.h | 15 + .../SampleChatAppWithShare/small.ico | Bin 0 -> 46227 bytes .../SampleChatAppWithShare/targetver.h | 6 + 11 files changed, 1355 insertions(+) create mode 100644 Samples/SampleChatAppWithShare/SampleChatAppWithShare.sln create mode 100644 Samples/SampleChatAppWithShare/SampleChatAppWithShare/Resource.h create mode 100644 Samples/SampleChatAppWithShare/SampleChatAppWithShare/SampleChatAppWithShare.cpp create mode 100644 Samples/SampleChatAppWithShare/SampleChatAppWithShare/SampleChatAppWithShare.h create mode 100644 Samples/SampleChatAppWithShare/SampleChatAppWithShare/SampleChatAppWithShare.ico create mode 100644 Samples/SampleChatAppWithShare/SampleChatAppWithShare/SampleChatAppWithShare.rc create mode 100644 Samples/SampleChatAppWithShare/SampleChatAppWithShare/SampleChatAppWithShare.vcxproj create mode 100644 Samples/SampleChatAppWithShare/SampleChatAppWithShare/SampleChatAppWithShare.vcxproj.filters create mode 100644 Samples/SampleChatAppWithShare/SampleChatAppWithShare/framework.h create mode 100644 Samples/SampleChatAppWithShare/SampleChatAppWithShare/small.ico create mode 100644 Samples/SampleChatAppWithShare/SampleChatAppWithShare/targetver.h diff --git a/Samples/SampleChatAppWithShare/SampleChatAppWithShare.sln b/Samples/SampleChatAppWithShare/SampleChatAppWithShare.sln new file mode 100644 index 0000000..8833b0a --- /dev/null +++ b/Samples/SampleChatAppWithShare/SampleChatAppWithShare.sln @@ -0,0 +1,37 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.14.36401.2 d17.14 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SampleChatAppWithShare", "SampleChatAppWithShare\SampleChatAppWithShare.vcxproj", "{DD6EC845-790A-4BD4-B638-AF0964704337}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|ARM64 = Debug|ARM64 + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|ARM64 = Release|ARM64 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {DD6EC845-790A-4BD4-B638-AF0964704337}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {DD6EC845-790A-4BD4-B638-AF0964704337}.Debug|ARM64.Build.0 = Debug|ARM64 + {DD6EC845-790A-4BD4-B638-AF0964704337}.Debug|x64.ActiveCfg = Debug|x64 + {DD6EC845-790A-4BD4-B638-AF0964704337}.Debug|x64.Build.0 = Debug|x64 + {DD6EC845-790A-4BD4-B638-AF0964704337}.Debug|x86.ActiveCfg = Debug|Win32 + {DD6EC845-790A-4BD4-B638-AF0964704337}.Debug|x86.Build.0 = Debug|Win32 + {DD6EC845-790A-4BD4-B638-AF0964704337}.Release|ARM64.ActiveCfg = Release|ARM64 + {DD6EC845-790A-4BD4-B638-AF0964704337}.Release|ARM64.Build.0 = Release|ARM64 + {DD6EC845-790A-4BD4-B638-AF0964704337}.Release|x64.ActiveCfg = Release|x64 + {DD6EC845-790A-4BD4-B638-AF0964704337}.Release|x64.Build.0 = Release|x64 + {DD6EC845-790A-4BD4-B638-AF0964704337}.Release|x86.ActiveCfg = Release|Win32 + {DD6EC845-790A-4BD4-B638-AF0964704337}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {6709D164-3EDF-4B1A-894A-2C561B2062CC} + EndGlobalSection +EndGlobal diff --git a/Samples/SampleChatAppWithShare/SampleChatAppWithShare/Resource.h b/Samples/SampleChatAppWithShare/SampleChatAppWithShare/Resource.h new file mode 100644 index 0000000..04a9f8f --- /dev/null +++ b/Samples/SampleChatAppWithShare/SampleChatAppWithShare/Resource.h @@ -0,0 +1,40 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by SampleChatAppWithShare.rc + +#define IDS_APP_TITLE 103 + +#define IDR_MAINFRAME 128 +#define IDD_SAMPLECHATAPPWITHSHARE_DIALOG 102 +#define IDD_ABOUTBOX 103 +#define IDM_ABOUT 104 +#define IDM_EXIT 105 +#define IDI_SAMPLECHATAPPWITHSHARE 107 +#define IDI_SMALL 108 +#define IDC_SAMPLECHATAPPWITHSHARE 109 +#define IDC_MYICON 2 + +// Chat Application Controls +#define IDC_CONTACTS_LIST 1001 +#define IDC_CHAT_DISPLAY 1002 +#define IDC_MESSAGE_INPUT 1003 +#define IDC_SEND_BUTTON 1004 +#define IDC_CONTACT_NAME 1005 +#define IDC_SHARE_FILE_BUTTON 1006 +#define IDC_SHARED_FILES_LIST 1007 + +#ifndef IDC_STATIC +#define IDC_STATIC -1 +#endif +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS + +#define _APS_NO_MFC 130 +#define _APS_NEXT_RESOURCE_VALUE 129 +#define _APS_NEXT_COMMAND_VALUE 32771 +#define _APS_NEXT_CONTROL_VALUE 1008 +#define _APS_NEXT_SYMED_VALUE 110 +#endif +#endif diff --git a/Samples/SampleChatAppWithShare/SampleChatAppWithShare/SampleChatAppWithShare.cpp b/Samples/SampleChatAppWithShare/SampleChatAppWithShare/SampleChatAppWithShare.cpp new file mode 100644 index 0000000..4abaee2 --- /dev/null +++ b/Samples/SampleChatAppWithShare/SampleChatAppWithShare/SampleChatAppWithShare.cpp @@ -0,0 +1,1008 @@ +// SampleChatAppWithShare.cpp : Defines the entry point for the application. +// + +#include "framework.h" +#include "SampleChatAppWithShare.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#pragma comment(lib, "comctl32.lib") +#pragma comment(lib, "comdlg32.lib") +#pragma comment(lib, "shell32.lib") +#pragma comment(lib, "gdiplus.lib") +#pragma comment(lib, "uxtheme.lib") + +#define MAX_LOADSTRING 100 + +// Modern UI Color Scheme +#define COLOR_PRIMARY RGB(64, 128, 255) // Modern blue +#define COLOR_PRIMARY_DARK RGB(45, 100, 220) // Darker blue for hover +#define COLOR_APP_BACKGROUND RGB(248, 249, 250) // Light gray background +#define COLOR_SURFACE RGB(255, 255, 255) // White surface +#define COLOR_TEXT_PRIMARY RGB(33, 37, 41) // Dark text +#define COLOR_TEXT_SECONDARY RGB(108, 117, 125) // Gray text +#define COLOR_BORDER RGB(222, 226, 230) // Light border +#define COLOR_HOVER RGB(248, 249, 250) // Hover background +#define COLOR_CHAT_BUBBLE_OUT RGB(0, 123, 255) // Outgoing message +#define COLOR_CHAT_BUBBLE_IN RGB(233, 236, 239) // Incoming message + +// Global Variables: +HINSTANCE hInst; // current instance +WCHAR szTitle[MAX_LOADSTRING]; // The title bar text +WCHAR szWindowClass[MAX_LOADSTRING]; // the main window class name + +// Chat Application Variables +HWND hContactsList; +HWND hChatDisplay; +HWND hMessageInput; +HWND hSendButton; +HWND hContactName; +HWND hShareFileButton; +HWND hSharedFilesList; + +// Modern UI Variables +HBRUSH hBrushBackground; +HBRUSH hBrushSurface; +HBRUSH hBrushPrimary; +HBRUSH hBrushHover; +HFONT hFontRegular; +HFONT hFontBold; +HFONT hFontTitle; +HPEN hPenBorder; + +struct SharedFile { + std::wstring fileName; + std::wstring filePath; + std::wstring sharedBy; + SYSTEMTIME timeShared; +}; + +struct Contact { + std::wstring name; + std::wstring lastMessage; + std::vector messages; + std::vector sharedFiles; + std::wstring status; + bool isOnline; +}; + +std::vector contacts; +std::map> chatHistory; +int selectedContactIndex = -1; + +// Forward declarations of functions included in this code module: +ATOM MyRegisterClass(HINSTANCE hInstance); +BOOL InitInstance(HINSTANCE, int); +LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); +LRESULT CALLBACK ModernButtonProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); +LRESULT CALLBACK ModernListBoxProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); +INT_PTR CALLBACK About(HWND, UINT, WPARAM, LPARAM); +void InitializeContacts(); +void CreateChatUI(HWND hWnd); +void LoadContactChat(int contactIndex); +void SendChatMessage(); +void AddMessageToChat(const std::wstring& message, bool isOutgoing); +void ShareFile(); +void AddSharedFileToChat(const SharedFile& file, bool isOutgoing); +void UpdateSharedFilesList(); +void OpenSharedFile(int fileIndex); +std::wstring GetFileExtensionIcon(const std::wstring& filePath); +std::wstring FormatFileSize(DWORD fileSize); +void InitializeModernUI(); +void CleanupModernUI(); +void DrawModernButton(HDC hdc, RECT rect, const std::wstring& text, bool isHovered, bool isPressed); +void DrawContactItem(HDC hdc, RECT rect, const Contact& contact, bool isSelected); + +// Window procedure storage for subclassing +WNDPROC originalButtonProc = nullptr; +WNDPROC originalListBoxProc = nullptr; + +int APIENTRY wWinMain(_In_ HINSTANCE hInstance, + _In_opt_ HINSTANCE hPrevInstance, + _In_ LPWSTR lpCmdLine, + _In_ int nCmdShow) +{ + UNREFERENCED_PARAMETER(hPrevInstance); + UNREFERENCED_PARAMETER(lpCmdLine); + + // Initialize COM for shell operations + CoInitialize(NULL); + + // Initialize common controls + INITCOMMONCONTROLSEX icex; + icex.dwSize = sizeof(INITCOMMONCONTROLSEX); + icex.dwICC = ICC_LISTVIEW_CLASSES | ICC_STANDARD_CLASSES; + InitCommonControlsEx(&icex); + + // Initialize global strings + LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING); + LoadStringW(hInstance, IDC_SAMPLECHATAPPWITHSHARE, szWindowClass, MAX_LOADSTRING); + MyRegisterClass(hInstance); + + // Initialize modern UI + InitializeModernUI(); + + // Initialize dummy contacts + InitializeContacts(); + + // Perform application initialization: + if (!InitInstance (hInstance, nCmdShow)) + { + return FALSE; + } + + HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_SAMPLECHATAPPWITHSHARE)); + + MSG msg; + + // Main message loop: + while (GetMessage(&msg, nullptr, 0, 0)) + { + if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)) + { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + } + + CoUninitialize(); + return (int) msg.wParam; +} + +// +// FUNCTION: MyRegisterClass() +// +// PURPOSE: Registers the window class. +// +ATOM MyRegisterClass(HINSTANCE hInstance) +{ + WNDCLASSEXW wcex; + + wcex.cbSize = sizeof(WNDCLASSEX); + + wcex.style = CS_HREDRAW | CS_VREDRAW; + wcex.lpfnWndProc = WndProc; + wcex.cbClsExtra = 0; + wcex.cbWndExtra = 0; + wcex.hInstance = hInstance; + wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_SAMPLECHATAPPWITHSHARE)); + wcex.hCursor = LoadCursor(nullptr, IDC_ARROW); + wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); + wcex.lpszMenuName = MAKEINTRESOURCEW(IDC_SAMPLECHATAPPWITHSHARE); + wcex.lpszClassName = szWindowClass; + wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL)); + + return RegisterClassExW(&wcex); +} + +// +// FUNCTION: InitInstance(HINSTANCE, int) +// +// PURPOSE: Saves instance handle and creates main window +// +// COMMENTS: +// +// In this function, we save the instance handle in a global variable and +// create and display the main program window. +// +BOOL InitInstance(HINSTANCE hInstance, int nCmdShow) +{ + hInst = hInstance; // Store instance handle in our global variable + + HWND hWnd = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW, + CW_USEDEFAULT, 0, 1200, 700, nullptr, nullptr, hInstance, nullptr); + + if (!hWnd) + { + return FALSE; + } + + ShowWindow(hWnd, nCmdShow); + UpdateWindow(hWnd); + + return TRUE; +} + +void InitializeContacts() +{ + contacts = { + {L"Alice Johnson", L"Hey, how are you?", {L"Alice: Hey, how are you?", L"You: I'm doing great, thanks!", L"Alice: That's wonderful to hear!"}, {}, L"Available", true}, + {L"Bob Smith", L"See you tomorrow!", {L"Bob: Are we still meeting tomorrow?", L"You: Yes, see you at 3 PM", L"Bob: See you tomorrow!"}, {}, L"In a meeting", true}, + {L"Carol Williams", L"Thanks for the help", {L"Carol: Could you help me with the project?", L"You: Of course! What do you need?", L"Carol: Thanks for the help"}, {}, L"Available", true}, + {L"David Brown", L"Great presentation!", {L"David: Great presentation today!", L"You: Thank you! I'm glad you liked it"}, {}, L"Away", false}, + {L"Emma Davis", L"Coffee later?", {L"Emma: Want to grab coffee later?", L"You: Sure! What time works for you?", L"Emma: Coffee later?"}, {}, L"Available", true}, + {L"Frank Miller", L"Happy Birthday!", {L"Frank: Happy Birthday!", L"You: Thank you so much!"}, {}, L"Busy", true}, + {L"Grace Wilson", L"Meeting rescheduled", {L"Grace: Meeting has been rescheduled to 4 PM", L"You: Got it, thanks for letting me know"}, {}, L"Available", true}, + {L"Henry Taylor", L"Weekend plans?", {L"Henry: Any plans for the weekend?", L"You: Nothing concrete yet", L"Henry: Weekend plans?"}, {}, L"Offline", false}, + {L"Ivy Anderson", L"Project update", {L"Ivy: Here's the project update you requested", L"You: Perfect, reviewing it now"}, {}, L"Available", true}, + {L"Jack Thompson", L"Game night Friday", {L"Jack: Game night this Friday?", L"You: Count me in!", L"Jack: Game night Friday"}, {}, L"Gaming", true}, + {L"Kate Garcia", L"Recipe sharing", {L"Kate: Loved that recipe you shared!", L"You: I'm so glad you enjoyed it!"}, {}, L"Cooking", true}, + {L"Leo Martinez", L"Workout buddy", {L"Leo: Gym session tomorrow morning?", L"You: Absolutely! 7 AM as usual?"}, {}, L"At the gym", true}, + {L"Mia Rodriguez", L"Book recommendation", {L"Mia: Any good book recommendations?", L"You: I just finished a great mystery novel"}, {}, L"Reading", true}, + {L"Noah Lee", L"Tech discussion", {L"Noah: Thoughts on the new framework?", L"You: It looks promising! Want to discuss over lunch?"}, {}, L"Coding", true}, + {L"Olivia Clark", L"Travel planning", {L"Olivia: Planning the vacation itinerary", L"You: Excited to see what you've planned!"}, {}, L"Traveling", false} + }; + + // Add some sample shared files to demonstrate the feature + SYSTEMTIME st; + GetSystemTime(&st); + + contacts[0].sharedFiles.push_back({L"Project_Proposal.docx", L"C:\\Documents\\Project_Proposal.docx", L"Alice", st}); + contacts[1].sharedFiles.push_back({L"Meeting_Notes.pdf", L"C:\\Documents\\Meeting_Notes.pdf", L"Bob", st}); + contacts[2].sharedFiles.push_back({L"Budget_Spreadsheet.xlsx", L"C:\\Documents\\Budget_Spreadsheet.xlsx", L"Carol", st}); +} + +void InitializeModernUI() +{ + // Create brushes for modern color scheme + hBrushBackground = CreateSolidBrush(COLOR_APP_BACKGROUND); + hBrushSurface = CreateSolidBrush(COLOR_SURFACE); + hBrushPrimary = CreateSolidBrush(COLOR_PRIMARY); + hBrushHover = CreateSolidBrush(COLOR_HOVER); + + // Create modern fonts + hFontRegular = CreateFont(16, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, + DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, + CLEARTYPE_QUALITY, DEFAULT_PITCH | FF_DONTCARE, L"Segoe UI"); + + hFontBold = CreateFont(16, 0, 0, 0, FW_SEMIBOLD, FALSE, FALSE, FALSE, + DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, + CLEARTYPE_QUALITY, DEFAULT_PITCH | FF_DONTCARE, L"Segoe UI"); + + hFontTitle = CreateFont(20, 0, 0, 0, FW_SEMIBOLD, FALSE, FALSE, FALSE, + DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, + CLEARTYPE_QUALITY, DEFAULT_PITCH | FF_DONTCARE, L"Segoe UI"); + + // Create pen for borders + hPenBorder = CreatePen(PS_SOLID, 1, COLOR_BORDER); + + // Initialize GDI+ + Gdiplus::GdiplusStartupInput gdiplusStartupInput; + ULONG_PTR gdiplusToken; + Gdiplus::GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL); +} + +void CleanupModernUI() +{ + // Cleanup GDI objects + if (hBrushBackground) DeleteObject(hBrushBackground); + if (hBrushSurface) DeleteObject(hBrushSurface); + if (hBrushPrimary) DeleteObject(hBrushPrimary); + if (hBrushHover) DeleteObject(hBrushHover); + if (hFontRegular) DeleteObject(hFontRegular); + if (hFontBold) DeleteObject(hFontBold); + if (hFontTitle) DeleteObject(hFontTitle); + if (hPenBorder) DeleteObject(hPenBorder); + + // Shutdown GDI+ + Gdiplus::GdiplusShutdown(NULL); +} + +void DrawModernButton(HDC hdc, RECT rect, const std::wstring& text, bool isHovered, bool isPressed) +{ + // Create rounded rectangle region + HRGN hRgn = CreateRoundRectRgn(rect.left, rect.top, rect.right, rect.bottom, 8, 8); + + // Fill background + HBRUSH hBrush = CreateSolidBrush(isPressed ? COLOR_PRIMARY_DARK : + isHovered ? COLOR_PRIMARY : COLOR_PRIMARY); + FillRgn(hdc, hRgn, hBrush); + DeleteObject(hBrush); + + // Draw border with a brush instead of pen + HBRUSH borderBrush = CreateSolidBrush(COLOR_BORDER); + FrameRgn(hdc, hRgn, borderBrush, 1, 1); + DeleteObject(borderBrush); + DeleteObject(hRgn); + + // Draw text + SetBkMode(hdc, TRANSPARENT); + SetTextColor(hdc, RGB(255, 255, 255)); + SelectObject(hdc, hFontBold); + + DrawText(hdc, text.c_str(), -1, &rect, DT_CENTER | DT_VCENTER | DT_SINGLELINE); +} + +void DrawContactItem(HDC hdc, RECT rect, const Contact& contact, bool isSelected) +{ + // Fill background + HBRUSH bgBrush = CreateSolidBrush(isSelected ? COLOR_HOVER : COLOR_SURFACE); + FillRect(hdc, &rect, bgBrush); + DeleteObject(bgBrush); + + // Draw avatar circle + int avatarSize = 40; + int avatarX = rect.left + 12; + int avatarY = rect.top + (rect.bottom - rect.top - avatarSize) / 2; + + HBRUSH avatarBrush = CreateSolidBrush(COLOR_PRIMARY); + HPEN avatarPen = CreatePen(PS_SOLID, 2, contact.isOnline ? RGB(34, 197, 94) : RGB(156, 163, 175)); + + SelectObject(hdc, avatarBrush); + SelectObject(hdc, avatarPen); + + Ellipse(hdc, avatarX, avatarY, avatarX + avatarSize, avatarY + avatarSize); + + DeleteObject(avatarBrush); + DeleteObject(avatarPen); + + // Draw contact name + SetBkMode(hdc, TRANSPARENT); + SetTextColor(hdc, COLOR_TEXT_PRIMARY); + SelectObject(hdc, hFontBold); + + RECT nameRect = {avatarX + avatarSize + 12, rect.top + 8, rect.right - 8, rect.top + 28}; + DrawText(hdc, contact.name.c_str(), -1, &nameRect, DT_LEFT | DT_VCENTER | DT_SINGLELINE); + + // Draw status + SetTextColor(hdc, COLOR_TEXT_SECONDARY); + SelectObject(hdc, hFontRegular); + + RECT statusRect = {avatarX + avatarSize + 12, rect.top + 30, rect.right - 8, rect.top + 48}; + DrawText(hdc, contact.status.c_str(), -1, &statusRect, DT_LEFT | DT_VCENTER | DT_SINGLELINE); + + // Draw last message preview + RECT msgRect = {avatarX + avatarSize + 12, rect.top + 50, rect.right - 8, rect.bottom - 8}; + DrawText(hdc, contact.lastMessage.c_str(), -1, &msgRect, DT_LEFT | DT_VCENTER | DT_SINGLELINE | DT_END_ELLIPSIS); +} + +void CreateChatUI(HWND hWnd) +{ + RECT rect; + GetClientRect(hWnd, &rect); + + int width = rect.right - rect.left; + int height = rect.bottom - rect.top; + + // Set window background to modern color + SetClassLongPtr(hWnd, GCLP_HBRBACKGROUND, (LONG_PTR)hBrushBackground); + + // Create contacts list (left panel) with modern styling + hContactsList = CreateWindowEx( + WS_EX_CLIENTEDGE, + L"LISTBOX", NULL, + WS_CHILD | WS_VISIBLE | WS_VSCROLL | LBS_NOTIFY | LBS_OWNERDRAWFIXED, + 20, 20, 280, height - 40, + hWnd, (HMENU)IDC_CONTACTS_LIST, hInst, NULL); + + // Set custom item height for contact list + ::SendMessage(hContactsList, LB_SETITEMHEIGHT, 0, 72); + + // Create contact name label with modern styling + hContactName = CreateWindow(L"STATIC", L"Select a contact to start chatting", + WS_CHILD | WS_VISIBLE | SS_LEFT, + 320, 20, 300, 30, + hWnd, (HMENU)IDC_CONTACT_NAME, hInst, NULL); + + // Create chat display area with modern styling + hChatDisplay = CreateWindowEx( + WS_EX_CLIENTEDGE, + L"EDIT", NULL, + WS_CHILD | WS_VISIBLE | WS_VSCROLL | ES_MULTILINE | ES_READONLY | ES_AUTOVSCROLL, + 320, 60, width - 600, height - 280, + hWnd, (HMENU)IDC_CHAT_DISPLAY, hInst, NULL); + + // Create shared files section header + CreateWindow(L"STATIC", L"Shared Files", + WS_CHILD | WS_VISIBLE | SS_LEFT, + width - 260, 20, 120, 30, + hWnd, NULL, hInst, NULL); + + // Create shared files list with modern styling + hSharedFilesList = CreateWindowEx( + WS_EX_CLIENTEDGE, + L"LISTBOX", NULL, + WS_CHILD | WS_VISIBLE | WS_VSCROLL | LBS_NOTIFY, + width - 260, 60, 240, height - 280, + hWnd, (HMENU)IDC_SHARED_FILES_LIST, hInst, NULL); + + // Create message input with placeholder styling + hMessageInput = CreateWindowEx( + WS_EX_CLIENTEDGE, + L"EDIT", NULL, + WS_CHILD | WS_VISIBLE | ES_MULTILINE | ES_AUTOVSCROLL, + 320, height - 200, width - 600, 60, + hWnd, (HMENU)IDC_MESSAGE_INPUT, hInst, NULL); + + // Create modern styled buttons + hSendButton = CreateWindow(L"BUTTON", L"Send", + WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | BS_OWNERDRAW, + 320, height - 130, 100, 45, + hWnd, (HMENU)IDC_SEND_BUTTON, hInst, NULL); + + hShareFileButton = CreateWindow(L"BUTTON", L"Share File", + WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | BS_OWNERDRAW, + 440, height - 130, 120, 45, + hWnd, (HMENU)IDC_SHARE_FILE_BUTTON, hInst, NULL); + + // Populate contacts list + for (const auto& contact : contacts) { + ::SendMessage(hContactsList, LB_ADDSTRING, 0, (LPARAM)contact.name.c_str()); + } + + // Apply modern fonts to controls + ::SendMessage(hContactName, WM_SETFONT, (WPARAM)hFontTitle, TRUE); + ::SendMessage(hChatDisplay, WM_SETFONT, (WPARAM)hFontRegular, TRUE); + ::SendMessage(hMessageInput, WM_SETFONT, (WPARAM)hFontRegular, TRUE); + ::SendMessage(hSharedFilesList, WM_SETFONT, (WPARAM)hFontRegular, TRUE); + + // Subclass buttons for custom drawing + originalButtonProc = (WNDPROC)SetWindowLongPtr(hSendButton, GWLP_WNDPROC, (LONG_PTR)ModernButtonProc); + SetWindowLongPtr(hShareFileButton, GWLP_WNDPROC, (LONG_PTR)ModernButtonProc); + + // Subclass contact list for custom drawing + originalListBoxProc = (WNDPROC)SetWindowLongPtr(hContactsList, GWLP_WNDPROC, (LONG_PTR)ModernListBoxProc); + + // Set modern background colors + SetClassLongPtr(hChatDisplay, GCLP_HBRBACKGROUND, (LONG_PTR)hBrushSurface); + SetClassLongPtr(hMessageInput, GCLP_HBRBACKGROUND, (LONG_PTR)hBrushSurface); + SetClassLongPtr(hSharedFilesList, GCLP_HBRBACKGROUND, (LONG_PTR)hBrushSurface); +} + +void LoadContactChat(int contactIndex) +{ + if (contactIndex < 0 || contactIndex >= (int)contacts.size()) return; + + selectedContactIndex = contactIndex; + const Contact& contact = contacts[contactIndex]; + + // Update contact name with status indicator + std::wstring headerText = contact.name + L" " + L" (" + contact.status + L")"; + SetWindowText(hContactName, headerText.c_str()); + + // Clear and populate chat display with better formatting + SetWindowText(hChatDisplay, L""); + + std::wstring chatText; + for (const auto& message : contact.messages) { + // Add timestamps and better message formatting + SYSTEMTIME st; + GetLocalTime(&st); + WCHAR timeStr[50]; + swprintf_s(timeStr, 50, L"[%02d:%02d] ", st.wHour, st.wMinute); + + if (message.find(L"You:") == 0) { + chatText += L" "; // Right align for your messages + chatText += timeStr + message + L"\r\n\r\n"; + } else { + chatText += timeStr + message + L"\r\n\r\n"; + } + } + + SetWindowText(hChatDisplay, chatText.c_str()); + + // Scroll to bottom + ::SendMessage(hChatDisplay, EM_SETSEL, -1, -1); + ::SendMessage(hChatDisplay, EM_SCROLLCARET, 0, 0); + + // Update shared files list + UpdateSharedFilesList(); + + // Refresh contact list to show selection + InvalidateRect(hContactsList, NULL, TRUE); +} + +void UpdateSharedFilesList() +{ + if (selectedContactIndex < 0 || selectedContactIndex >= (int)contacts.size()) return; + + const Contact& contact = contacts[selectedContactIndex]; + + // Clear the list + ::SendMessage(hSharedFilesList, LB_RESETCONTENT, 0, 0); + + // Add shared files with file type icons + for (const auto& file : contact.sharedFiles) { + std::wstring icon = GetFileExtensionIcon(file.fileName); + std::wstring displayText = icon + L" " + file.fileName; + displayText += L"\n ?? " + file.sharedBy; + ::SendMessage(hSharedFilesList, LB_ADDSTRING, 0, (LPARAM)displayText.c_str()); + } +} + +void AddMessageToChat(const std::wstring& message, bool isOutgoing) +{ + if (selectedContactIndex < 0 || selectedContactIndex >= (int)contacts.size()) return; + + Contact& contact = contacts[selectedContactIndex]; + + // Add timestamp to message + SYSTEMTIME st; + GetLocalTime(&st); + WCHAR timeStr[50]; + swprintf_s(timeStr, 50, L"[%02d:%02d] ", st.wHour, st.wMinute); + + std::wstring formattedMessage; + if (isOutgoing) { + formattedMessage = L"You: " + message + L" ?"; + } else { + formattedMessage = contact.name + L": " + message; + } + + contact.messages.push_back(formattedMessage); + contact.lastMessage = message.length() > 50 ? message.substr(0, 47) + L"..." : message; + + // Update chat display + LoadContactChat(selectedContactIndex); + + // Refresh contacts list to update last message preview + InvalidateRect(hContactsList, NULL, TRUE); +} + +void AddSharedFileToChat(const SharedFile& file, bool isOutgoing) +{ + if (selectedContactIndex < 0 || selectedContactIndex >= (int)contacts.size()) return; + + Contact& contact = contacts[selectedContactIndex]; + std::wstring sharer = isOutgoing ? L"You" : contact.name; + std::wstring formattedMessage = sharer + L" shared: " + file.fileName; + + contact.messages.push_back(formattedMessage); + + // Add to shared files list + if (isOutgoing) { + SharedFile newFile = file; + newFile.sharedBy = L"You"; + contact.sharedFiles.push_back(newFile); + } + + // Update both chat display and shared files list + LoadContactChat(selectedContactIndex); +} + +void ShareFile() +{ + if (selectedContactIndex < 0) { + MessageBox(NULL, L"Please select a contact to share files with. ??", L"No Contact Selected", MB_OK | MB_ICONWARNING); + return; + } + + OPENFILENAME ofn; + WCHAR szFile[260] = {0}; + + ZeroMemory(&ofn, sizeof(ofn)); + ofn.lStructSize = sizeof(ofn); + ofn.lpstrFile = szFile; + ofn.nMaxFile = sizeof(szFile); + ofn.lpstrFilter = L"All Files\0*.*\0?? Text Files\0*.TXT\0?? Document Files\0*.DOC;*.DOCX\0??? Image Files\0*.BMP;*.JPG;*.PNG;*.GIF\0?? PDF Files\0*.PDF\0?? Excel Files\0*.XLS;*.XLSX\0"; + ofn.nFilterIndex = 1; + ofn.lpstrFileTitle = NULL; + ofn.nMaxFileTitle = 0; + ofn.lpstrInitialDir = NULL; + ofn.lpstrTitle = L"Select File to Share"; + ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_EXPLORER; + + if (GetOpenFileName(&ofn)) { + // Extract file name from full path + std::wstring fullPath(szFile); + size_t lastSlash = fullPath.find_last_of(L"\\");// Create shared file entry + std::wstring fileName; + if (lastSlash != std::wstring::npos) { + fileName = fullPath.substr(lastSlash + 1); + } else { + fileName = fullPath; + } + + // Create shared file entry + SharedFile newFile; + newFile.fileName = fileName; + newFile.filePath = fullPath; + newFile.sharedBy = L"You"; + GetSystemTime(&newFile.timeShared); + + // Add to chat and shared files + AddSharedFileToChat(newFile, true); + + // Simulate auto-reply from contact acknowledging the file + SetTimer(GetParent(hMessageInput), 2, 1500, NULL); + } +} + +void OpenSharedFile(int fileIndex) +{ + if (selectedContactIndex < 0 || selectedContactIndex >= (int)contacts.size()) return; + + const Contact& contact = contacts[selectedContactIndex]; + if (fileIndex < 0 || fileIndex >= (int)contact.sharedFiles.size()) return; + + const SharedFile& file = contact.sharedFiles[fileIndex]; + + // Enhanced file info display with emojis and better formatting + std::wstring message = L"File Information\n\n"; + message += L"Name: " + file.fileName + L"\n"; + message += L"Path: " + file.filePath + L"\n"; + message += L"Shared by: " + file.sharedBy + L"\n"; + + WCHAR timeStr[100]; + swprintf_s(timeStr, 100, L"Shared on: %02d/%02d/%04d at %02d:%02d", + file.timeShared.wMonth, file.timeShared.wDay, file.timeShared.wYear, + file.timeShared.wHour, file.timeShared.wMinute); + message += timeStr; + message += L"\n\nTip: In a real application, this file would open automatically!"; + + MessageBox(NULL, message.c_str(), L"Shared File Information", MB_OK | MB_ICONINFORMATION); + + // In a real application, you would use ShellExecute to open the file: + // ShellExecute(NULL, L"open", file.filePath.c_str(), NULL, NULL, SW_SHOWNORMAL); +} + +std::wstring GetFileExtensionIcon(const std::wstring& filePath) +{ + size_t dotPos = filePath.find_last_of(L"."); + if (dotPos != std::wstring::npos) { + std::wstring ext = filePath.substr(dotPos + 1); + std::transform(ext.begin(), ext.end(), ext.begin(), ::towlower); + + if (ext == L"txt") return L"??"; + if (ext == L"doc" || ext == L"docx") return L"??"; + if (ext == L"pdf") return L"??"; + if (ext == L"xls" || ext == L"xlsx") return L"??"; + if (ext == L"jpg" || ext == L"png" || ext == L"gif" || ext == L"bmp") return L"???"; + if (ext == L"mp3" || ext == L"wav") return L"??"; + if (ext == L"mp4" || ext == L"avi") return L"??"; + if (ext == L"zip" || ext == L"rar") return L"???"; + if (ext == L"exe" || ext == L"msi") return L"??"; + } + return L"??"; +} + +std::wstring FormatFileSize(DWORD fileSize) +{ + const DWORD KB = 1024; + const DWORD MB = KB * 1024; + const DWORD GB = MB * 1024; + + WCHAR buffer[50]; + if (fileSize >= GB) { + swprintf_s(buffer, 50, L"%.1f GB", (double)fileSize / GB); + } else if (fileSize >= MB) { + swprintf_s(buffer, 50, L"%.1f MB", (double)fileSize / MB); + } else if (fileSize >= KB) { + swprintf_s(buffer, 50, L"%.1f KB", (double)fileSize / KB); + } else { + swprintf_s(buffer, 50, L"%d bytes", fileSize); + } + return std::wstring(buffer); +} + +// Modern button subclass procedure +LRESULT CALLBACK ModernButtonProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + static bool isHovered = false; + static bool isPressed = false; + + switch (msg) + { + case WM_PAINT: + { + PAINTSTRUCT ps; + HDC hdc = BeginPaint(hWnd, &ps); + + RECT rect; + GetClientRect(hWnd, &rect); + + WCHAR text[256]; + GetWindowText(hWnd, text, 256); + + DrawModernButton(hdc, rect, std::wstring(text), isHovered, isPressed); + + EndPaint(hWnd, &ps); + return 0; + } + + case WM_MOUSEMOVE: + if (!isHovered) + { + isHovered = true; + InvalidateRect(hWnd, NULL, FALSE); + + TRACKMOUSEEVENT tme = {}; + tme.cbSize = sizeof(tme); + tme.dwFlags = TME_LEAVE; + tme.hwndTrack = hWnd; + TrackMouseEvent(&tme); + } + break; + + case WM_MOUSELEAVE: + isHovered = false; + InvalidateRect(hWnd, NULL, FALSE); + break; + + case WM_LBUTTONDOWN: + isPressed = true; + InvalidateRect(hWnd, NULL, FALSE); + SetCapture(hWnd); + break; + + case WM_LBUTTONUP: + if (isPressed) + { + isPressed = false; + InvalidateRect(hWnd, NULL, FALSE); + ReleaseCapture(); + + // Send click notification to parent + HWND hParent = GetParent(hWnd); + int controlId = GetDlgCtrlID(hWnd); + ::SendMessage(hParent, WM_COMMAND, MAKEWPARAM(controlId, BN_CLICKED), (LPARAM)hWnd); + } + break; + } + + return CallWindowProc(originalButtonProc, hWnd, msg, wParam, lParam); +} + +// Modern listbox subclass procedure +LRESULT CALLBACK ModernListBoxProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + switch (msg) + { + case WM_PAINT: + if (hWnd == hContactsList) + { + PAINTSTRUCT ps; + HDC hdc = BeginPaint(hWnd, &ps); + + RECT clientRect; + GetClientRect(hWnd, &clientRect); + + // Fill background + FillRect(hdc, &clientRect, hBrushSurface); + + int itemCount = contacts.size(); + int itemHeight = 72; + int selectedIndex = ::SendMessage(hWnd, LB_GETCURSEL, 0, 0); + + for (int i = 0; i < itemCount; i++) + { + RECT itemRect = {0, i * itemHeight, clientRect.right, (i + 1) * itemHeight}; + DrawContactItem(hdc, itemRect, contacts[i], i == selectedIndex); + } + + EndPaint(hWnd, &ps); + return 0; + } + break; + } + + return CallWindowProc(originalListBoxProc, hWnd, msg, wParam, lParam); +} + +// +// FUNCTION: WndProc(HWND, UINT, WPARAM, LPARAM) +// +// PURPOSE: Processes messages for the main window. +// +// WM_COMMAND - process the application menu +// WM_PAINT - Paint the main window +// WM_DESTROY - post a quit message and return +// +// +LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + switch (message) + { + case WM_CREATE: + CreateChatUI(hWnd); + break; + + case WM_SIZE: + { + // Resize chat UI elements when window is resized with modern spacing + RECT rect; + GetClientRect(hWnd, &rect); + int width = rect.right - rect.left; + int height = rect.bottom - rect.top; + + if (hContactsList) { + SetWindowPos(hContactsList, NULL, 20, 20, 280, height - 40, SWP_NOZORDER); + } + if (hChatDisplay) { + SetWindowPos(hChatDisplay, NULL, 320, 60, width - 600, height - 280, SWP_NOZORDER); + } + if (hSharedFilesList) { + SetWindowPos(hSharedFilesList, NULL, width - 260, 60, 240, height - 280, SWP_NOZORDER); + } + if (hMessageInput) { + SetWindowPos(hMessageInput, NULL, 320, height - 200, width - 600, 60, SWP_NOZORDER); + } + if (hSendButton) { + SetWindowPos(hSendButton, NULL, 320, height - 130, 100, 45, SWP_NOZORDER); + } + if (hShareFileButton) { + SetWindowPos(hShareFileButton, NULL, 440, height - 130, 120, 45, SWP_NOZORDER); + } + } + break; + + case WM_CTLCOLORSTATIC: + { + HDC hdcStatic = (HDC)wParam; + SetTextColor(hdcStatic, COLOR_TEXT_PRIMARY); + SetBkColor(hdcStatic, COLOR_APP_BACKGROUND); + return (INT_PTR)hBrushBackground; + } + + case WM_CTLCOLOREDIT: + { + HDC hdcEdit = (HDC)wParam; + SetTextColor(hdcEdit, COLOR_TEXT_PRIMARY); + SetBkColor(hdcEdit, COLOR_SURFACE); + return (INT_PTR)hBrushSurface; + } + + case WM_CTLCOLORLISTBOX: + { + HDC hdcList = (HDC)wParam; + SetTextColor(hdcList, COLOR_TEXT_PRIMARY); + SetBkColor(hdcList, COLOR_SURFACE); + return (INT_PTR)hBrushSurface; + } + + case WM_PAINT: + { + PAINTSTRUCT ps; + HDC hdc = BeginPaint(hWnd, &ps); + + // Fill the main window background with modern color + RECT rect; + GetClientRect(hWnd, &rect); + FillRect(hdc, &rect, hBrushBackground); + + EndPaint(hWnd, &ps); + } + break; + + case WM_COMMAND: + { + int wmId = LOWORD(wParam); + int wmEvent = HIWORD(wParam); + + // Parse the menu selections: + switch (wmId) + { + case IDM_ABOUT: + DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About); + break; + case IDM_EXIT: + DestroyWindow(hWnd); + break; + case IDC_CONTACTS_LIST: + if (wmEvent == LBN_SELCHANGE) { + int selectedIndex = (int)::SendMessage(hContactsList, LB_GETCURSEL, 0, 0); + LoadContactChat(selectedIndex); + } + break; + case IDC_SEND_BUTTON: + SendChatMessage(); + break; + case IDC_SHARE_FILE_BUTTON: + ShareFile(); + break; + case IDC_SHARED_FILES_LIST: + if (wmEvent == LBN_DBLCLK) { + int selectedFile = (int)::SendMessage(hSharedFilesList, LB_GETCURSEL, 0, 0); + OpenSharedFile(selectedFile); + } + break; + case IDC_MESSAGE_INPUT: + if (wmEvent == EN_CHANGE) { + // Enable/disable send button based on input with visual feedback + WCHAR buffer[1024]; + GetWindowText(hMessageInput, buffer, 1024); + bool hasText = wcslen(buffer) > 0; + EnableWindow(hSendButton, hasText); + InvalidateRect(hSendButton, NULL, FALSE); + } + break; + default: + return DefWindowProc(hWnd, message, wParam, lParam); + } + } + break; + + case WM_TIMER: + { + if (wParam == 1 && selectedContactIndex >= 0) { + KillTimer(hWnd, 1); + + // Auto-reply from the selected contact with more variety + std::vector autoReplies = { + L"Got it!", + L"Thanks for letting me know!", + L"Sounds good!", + L"I'll get back to you soon.", + L"Perfect!", + L"Absolutely!", + L"Let me think about it.", + L"Great idea!" + }; + + int replyIndex = rand() % autoReplies.size(); + AddMessageToChat(autoReplies[replyIndex], false); + } + else if (wParam == 2 && selectedContactIndex >= 0) { + KillTimer(hWnd, 2); + + // Auto-reply acknowledging the shared file with emojis + std::vector fileReplies = { + L"Thanks for sharing the file!", + L"Got the file, will check it out.", + L"File received, thanks! ?", + L"Perfect timing, I needed this file.", + L"Awesome, downloading now!" + }; + + int replyIndex = rand() % fileReplies.size(); + AddMessageToChat(fileReplies[replyIndex], false); + } + } + break; + + case WM_KEYDOWN: + { + if (GetFocus() == hMessageInput && wParam == VK_RETURN) { + if (!(GetKeyState(VK_SHIFT) & 0x8000)) { + // Enter without Shift sends the message + SendChatMessage(); + return 0; + } + } + } + break; + + case WM_DESTROY: + CleanupModernUI(); + PostQuitMessage(0); + break; + + default: + return DefWindowProc(hWnd, message, wParam, lParam); + } + return 0; +} + +// Message handler for about box. +INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) +{ + UNREFERENCED_PARAMETER(lParam); + switch (message) + { + case WM_INITDIALOG: + return (INT_PTR)TRUE; + + case WM_COMMAND: + if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL) + { + EndDialog(hDlg, LOWORD(wParam)); + return (INT_PTR)TRUE; + } + break; + } + return (INT_PTR)FALSE; +} + +void SendChatMessage() +{ + if (selectedContactIndex < 0) return; + + WCHAR buffer[1024]; + GetWindowText(hMessageInput, buffer, 1024); + + std::wstring message(buffer); + if (!message.empty()) { + AddMessageToChat(message, true); + SetWindowText(hMessageInput, L""); + + // Simulate auto-reply after a short delay + SetTimer(GetParent(hMessageInput), 1, 2000, NULL); + } +} diff --git a/Samples/SampleChatAppWithShare/SampleChatAppWithShare/SampleChatAppWithShare.h b/Samples/SampleChatAppWithShare/SampleChatAppWithShare/SampleChatAppWithShare.h new file mode 100644 index 0000000..d00d47e --- /dev/null +++ b/Samples/SampleChatAppWithShare/SampleChatAppWithShare/SampleChatAppWithShare.h @@ -0,0 +1,3 @@ +#pragma once + +#include "resource.h" diff --git a/Samples/SampleChatAppWithShare/SampleChatAppWithShare/SampleChatAppWithShare.ico b/Samples/SampleChatAppWithShare/SampleChatAppWithShare/SampleChatAppWithShare.ico new file mode 100644 index 0000000000000000000000000000000000000000..b3ec03bd617f32e58128fa977fd6ac9605124f4b GIT binary patch literal 46227 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 literal 0 HcmV?d00001 diff --git a/Samples/SampleChatAppWithShare/SampleChatAppWithShare/SampleChatAppWithShare.vcxproj b/Samples/SampleChatAppWithShare/SampleChatAppWithShare/SampleChatAppWithShare.vcxproj new file mode 100644 index 0000000..9f6f5e2 --- /dev/null +++ b/Samples/SampleChatAppWithShare/SampleChatAppWithShare/SampleChatAppWithShare.vcxproj @@ -0,0 +1,197 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + Debug + ARM64 + + + Release + ARM64 + + + + 17.0 + Win32Proj + {dd6ec845-790a-4bd4-b638-af0964704337} + SampleChatAppWithShare + 10.0 + + + + Application + true + v143 + Unicode + + + Application + false + v143 + true + Unicode + + + Application + true + v143 + Unicode + + + Application + false + v143 + true + Unicode + + + Application + true + v143 + Unicode + + + Application + false + v143 + true + Unicode + + + + + + + + + + + + + + + + + + + + + + + + + + + + Level3 + true + WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions) + true + + + Windows + true + + + + + Level3 + true + true + true + WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions) + true + + + Windows + true + + + + + Level3 + true + _DEBUG;_WINDOWS;%(PreprocessorDefinitions) + true + + + Windows + true + + + + + Level3 + true + true + true + NDEBUG;_WINDOWS;%(PreprocessorDefinitions) + true + + + Windows + true + + + + + Level3 + true + _DEBUG;_WINDOWS;%(PreprocessorDefinitions) + true + + + Windows + true + + + + + Level3 + true + true + true + NDEBUG;_WINDOWS;%(PreprocessorDefinitions) + true + + + Windows + true + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Samples/SampleChatAppWithShare/SampleChatAppWithShare/SampleChatAppWithShare.vcxproj.filters b/Samples/SampleChatAppWithShare/SampleChatAppWithShare/SampleChatAppWithShare.vcxproj.filters new file mode 100644 index 0000000..a39e463 --- /dev/null +++ b/Samples/SampleChatAppWithShare/SampleChatAppWithShare/SampleChatAppWithShare.vcxproj.filters @@ -0,0 +1,49 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + + Source Files + + + + + Resource Files + + + + + Resource Files + + + Resource Files + + + \ No newline at end of file diff --git a/Samples/SampleChatAppWithShare/SampleChatAppWithShare/framework.h b/Samples/SampleChatAppWithShare/SampleChatAppWithShare/framework.h new file mode 100644 index 0000000..33a6be6 --- /dev/null +++ b/Samples/SampleChatAppWithShare/SampleChatAppWithShare/framework.h @@ -0,0 +1,15 @@ +// header.h : include file for standard system include files, +// or project specific include files +// + +#pragma once + +#include "targetver.h" +#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers +// Windows Header Files +#include +// C RunTime Header Files +#include +#include +#include +#include diff --git a/Samples/SampleChatAppWithShare/SampleChatAppWithShare/small.ico b/Samples/SampleChatAppWithShare/SampleChatAppWithShare/small.ico new file mode 100644 index 0000000000000000000000000000000000000000..b3ec03bd617f32e58128fa977fd6ac9605124f4b GIT binary patch literal 46227 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 From 5e465875390a57867a9a149d827cbaddbdc73aa4 Mon Sep 17 00:00:00 2001 From: Ravish Roshan Date: Tue, 23 Sep 2025 16:33:08 +0530 Subject: [PATCH 03/20] Code refactoring --- .../SampleChatAppWithShare/ChatManager.cpp | 132 ++++ .../SampleChatAppWithShare/ChatManager.h | 10 + .../SampleChatAppWithShare/ChatModels.cpp | 48 ++ .../SampleChatAppWithShare/ChatModels.h | 32 + .../SampleChatAppWithShare/FileManager.cpp | 162 ++++ .../SampleChatAppWithShare/FileManager.h | 13 + .../SampleChatAppWithShare/ModernUI.cpp | 128 +++ .../SampleChatAppWithShare/ModernUI.h | 21 + .../SampleChatAppWithShare.cpp | 744 +----------------- .../SampleChatAppWithShare.exe.manifest | 9 + .../SampleChatAppWithShare.vcxproj | 16 + .../SampleChatAppWithShare.vcxproj.filters | 42 + .../SampleChatAppWithShare/UIConstants.h | 23 + .../SampleChatAppWithShare/UIManager.cpp | 142 ++++ .../SampleChatAppWithShare/UIManager.h | 17 + .../SampleChatAppWithShare/WindowProcs.cpp | 127 +++ .../SampleChatAppWithShare/WindowProcs.h | 14 + 17 files changed, 948 insertions(+), 732 deletions(-) create mode 100644 Samples/SampleChatAppWithShare/SampleChatAppWithShare/ChatManager.cpp create mode 100644 Samples/SampleChatAppWithShare/SampleChatAppWithShare/ChatManager.h create mode 100644 Samples/SampleChatAppWithShare/SampleChatAppWithShare/ChatModels.cpp create mode 100644 Samples/SampleChatAppWithShare/SampleChatAppWithShare/ChatModels.h create mode 100644 Samples/SampleChatAppWithShare/SampleChatAppWithShare/FileManager.cpp create mode 100644 Samples/SampleChatAppWithShare/SampleChatAppWithShare/FileManager.h create mode 100644 Samples/SampleChatAppWithShare/SampleChatAppWithShare/ModernUI.cpp create mode 100644 Samples/SampleChatAppWithShare/SampleChatAppWithShare/ModernUI.h create mode 100644 Samples/SampleChatAppWithShare/SampleChatAppWithShare/SampleChatAppWithShare.exe.manifest create mode 100644 Samples/SampleChatAppWithShare/SampleChatAppWithShare/UIConstants.h create mode 100644 Samples/SampleChatAppWithShare/SampleChatAppWithShare/UIManager.cpp create mode 100644 Samples/SampleChatAppWithShare/SampleChatAppWithShare/UIManager.h create mode 100644 Samples/SampleChatAppWithShare/SampleChatAppWithShare/WindowProcs.cpp create mode 100644 Samples/SampleChatAppWithShare/SampleChatAppWithShare/WindowProcs.h diff --git a/Samples/SampleChatAppWithShare/SampleChatAppWithShare/ChatManager.cpp b/Samples/SampleChatAppWithShare/SampleChatAppWithShare/ChatManager.cpp new file mode 100644 index 0000000..b00ebb6 --- /dev/null +++ b/Samples/SampleChatAppWithShare/SampleChatAppWithShare/ChatManager.cpp @@ -0,0 +1,132 @@ +#include "ChatManager.h" +#include "ChatModels.h" +#include "FileManager.h" +#include "UIManager.h" +#include +#include +#include + +void LoadContactChat(int contactIndex) +{ + if (!IsValidContactIndex(contactIndex)) return; + + selectedContactIndex = contactIndex; + const Contact& contact = contacts[contactIndex]; + + // Update contact name with status indicator + std::wstring headerText = contact.name + L" " + L" (" + contact.status + L")"; + SetWindowText(hContactName, headerText.c_str()); + + // Clear and populate chat display with better formatting + SetWindowText(hChatDisplay, L""); + + std::wstring chatText; + for (const auto& message : contact.messages) { + // Add timestamps and better message formatting + SYSTEMTIME st; + GetLocalTime(&st); + WCHAR timeStr[50]; + swprintf_s(timeStr, 50, L"[%02d:%02d] ", st.wHour, st.wMinute); + + if (message.find(L"You:") == 0) { + chatText += L" "; // Right align for your messages + chatText += timeStr + message + L"\r\n\r\n"; + } else { + chatText += timeStr + message + L"\r\n\r\n"; + } + } + + SetWindowText(hChatDisplay, chatText.c_str()); + + // Scroll to bottom + ::SendMessage(hChatDisplay, EM_SETSEL, -1, -1); + ::SendMessage(hChatDisplay, EM_SCROLLCARET, 0, 0); + + // Update shared files list + UpdateSharedFilesList(); + + // Refresh contact list to show selection + InvalidateRect(hContactsList, NULL, TRUE); +} + +void AddMessageToChat(const std::wstring& message, bool isOutgoing) +{ + Contact* contact = GetSelectedContact(); + if (!contact) return; + + // Add timestamp to message + SYSTEMTIME st; + GetLocalTime(&st); + WCHAR timeStr[50]; + swprintf_s(timeStr, 50, L"[%02d:%02d] ", st.wHour, st.wMinute); + + std::wstring formattedMessage; + if (isOutgoing) { + formattedMessage = L"You: " + message + L" ??"; + } else { + formattedMessage = contact->name + L": " + message; + } + + contact->messages.push_back(formattedMessage); + contact->lastMessage = message.length() > 50 ? message.substr(0, 47) + L"..." : message; + + // Update chat display + LoadContactChat(selectedContactIndex); + + // Refresh contacts list to update last message preview + InvalidateRect(hContactsList, NULL, TRUE); +} + +void SendChatMessage() +{ + if (selectedContactIndex < 0) return; + + WCHAR buffer[1024]; + GetWindowText(hMessageInput, buffer, 1024); + + std::wstring message(buffer); + if (!message.empty()) { + AddMessageToChat(message, true); + SetWindowText(hMessageInput, L""); + + // Simulate auto-reply after a short delay + SetTimer(GetParent(hMessageInput), 1, 2000, NULL); + } +} + +void ProcessAutoReply(HWND hWnd, int timerType) +{ + if (selectedContactIndex < 0) return; + + KillTimer(hWnd, timerType); + + if (timerType == 1) { + // Auto-reply from the selected contact with more variety + std::vector autoReplies = { + L"Got it!", + L"Thanks for letting me know!", + L"Sounds good!", + L"I'll get back to you soon.", + L"Perfect!", + L"Absolutely!", + L"Let me think about it.", + L"Great idea!" + }; + + int replyIndex = rand() % autoReplies.size(); + AddMessageToChat(autoReplies[replyIndex], false); + } + else if (timerType == 2) { + // Auto-reply acknowledging the shared file + std::vector fileReplies = { + L"Thanks for sharing the file!", + L"Got the file, will check it out.", + L"File received, thanks! ??", + L"Perfect timing, I needed this file.", + L"Awesome, downloading now!" + }; + + int replyIndex = rand() % fileReplies.size(); + AddMessageToChat(fileReplies[replyIndex], false); + } +} \ No newline at end of file diff --git a/Samples/SampleChatAppWithShare/SampleChatAppWithShare/ChatManager.h b/Samples/SampleChatAppWithShare/SampleChatAppWithShare/ChatManager.h new file mode 100644 index 0000000..daefb88 --- /dev/null +++ b/Samples/SampleChatAppWithShare/SampleChatAppWithShare/ChatManager.h @@ -0,0 +1,10 @@ +#pragma once + +#include +#include + +// Chat management functions +void LoadContactChat(int contactIndex); +void SendChatMessage(); +void AddMessageToChat(const std::wstring& message, bool isOutgoing); +void ProcessAutoReply(HWND hWnd, int timerType); \ No newline at end of file diff --git a/Samples/SampleChatAppWithShare/SampleChatAppWithShare/ChatModels.cpp b/Samples/SampleChatAppWithShare/SampleChatAppWithShare/ChatModels.cpp new file mode 100644 index 0000000..db4f520 --- /dev/null +++ b/Samples/SampleChatAppWithShare/SampleChatAppWithShare/ChatModels.cpp @@ -0,0 +1,48 @@ +#include "ChatModels.h" + +// Global data definitions +std::vector contacts; +std::map> chatHistory; +int selectedContactIndex = -1; + +void InitializeContacts() +{ + contacts = { + {L"Alice Johnson", L"Hey, how are you?", {L"Alice: Hey, how are you?", L"You: I'm doing great, thanks!", L"Alice: That's wonderful to hear!"}, {}, L"Available", true}, + {L"Bob Smith", L"See you tomorrow!", {L"Bob: Are we still meeting tomorrow?", L"You: Yes, see you at 3 PM", L"Bob: See you tomorrow!"}, {}, L"In a meeting", true}, + {L"Carol Williams", L"Thanks for the help", {L"Carol: Could you help me with the project?", L"You: Of course! What do you need?", L"Carol: Thanks for the help"}, {}, L"Available", true}, + {L"David Brown", L"Great presentation!", {L"David: Great presentation today!", L"You: Thank you! I'm glad you liked it"}, {}, L"Away", false}, + {L"Emma Davis", L"Coffee later?", {L"Emma: Want to grab coffee later?", L"You: Sure! What time works for you?", L"Emma: Coffee later?"}, {}, L"Available", true}, + {L"Frank Miller", L"Happy Birthday!", {L"Frank: Happy Birthday!", L"You: Thank you so much!"}, {}, L"Busy", true}, + {L"Grace Wilson", L"Meeting rescheduled", {L"Grace: Meeting has been rescheduled to 4 PM", L"You: Got it, thanks for letting me know"}, {}, L"Available", true}, + {L"Henry Taylor", L"Weekend plans?", {L"Henry: Any plans for the weekend?", L"You: Nothing concrete yet", L"Henry: Weekend plans?"}, {}, L"Offline", false}, + {L"Ivy Anderson", L"Project update", {L"Ivy: Here's the project update you requested", L"You: Perfect, reviewing it now"}, {}, L"Available", true}, + {L"Jack Thompson", L"Game night Friday", {L"Jack: Game night this Friday?", L"You: Count me in!", L"Jack: Game night Friday"}, {}, L"Gaming", true}, + {L"Kate Garcia", L"Recipe sharing", {L"Kate: Loved that recipe you shared!", L"You: I'm so glad you enjoyed it!"}, {}, L"Cooking", true}, + {L"Leo Martinez", L"Workout buddy", {L"Leo: Gym session tomorrow morning?", L"You: Absolutely! 7 AM as usual?"}, {}, L"At the gym", true}, + {L"Mia Rodriguez", L"Book recommendation", {L"Mia: Any good book recommendations?", L"You: I just finished a great mystery novel"}, {}, L"Reading", true}, + {L"Noah Lee", L"Tech discussion", {L"Noah: Thoughts on the new framework?", L"You: It looks promising! Want to discuss over lunch?"}, {}, L"Coding", true}, + {L"Olivia Clark", L"Travel planning", {L"Olivia: Planning the vacation itinerary", L"You: Excited to see what you've planned!"}, {}, L"Traveling", false} + }; + + // Add some sample shared files to demonstrate the feature + SYSTEMTIME st; + GetSystemTime(&st); + + contacts[0].sharedFiles.push_back({L"Project_Proposal.docx", L"C:\\Documents\\Project_Proposal.docx", L"Alice", st}); + contacts[1].sharedFiles.push_back({L"Meeting_Notes.pdf", L"C:\\Documents\\Meeting_Notes.pdf", L"Bob", st}); + contacts[2].sharedFiles.push_back({L"Budget_Spreadsheet.xlsx", L"C:\\Documents\\Budget_Spreadsheet.xlsx", L"Carol", st}); +} + +Contact* GetSelectedContact() +{ + if (IsValidContactIndex(selectedContactIndex)) { + return &contacts[selectedContactIndex]; + } + return nullptr; +} + +bool IsValidContactIndex(int index) +{ + return index >= 0 && index < (int)contacts.size(); +} \ No newline at end of file diff --git a/Samples/SampleChatAppWithShare/SampleChatAppWithShare/ChatModels.h b/Samples/SampleChatAppWithShare/SampleChatAppWithShare/ChatModels.h new file mode 100644 index 0000000..3fb7531 --- /dev/null +++ b/Samples/SampleChatAppWithShare/SampleChatAppWithShare/ChatModels.h @@ -0,0 +1,32 @@ +#pragma once + +#include +#include +#include +#include + +struct SharedFile { + std::wstring fileName; + std::wstring filePath; + std::wstring sharedBy; + SYSTEMTIME timeShared; +}; + +struct Contact { + std::wstring name; + std::wstring lastMessage; + std::vector messages; + std::vector sharedFiles; + std::wstring status; + bool isOnline; +}; + +// Global data +extern std::vector contacts; +extern std::map> chatHistory; +extern int selectedContactIndex; + +// Contact management functions +void InitializeContacts(); +Contact* GetSelectedContact(); +bool IsValidContactIndex(int index); \ No newline at end of file diff --git a/Samples/SampleChatAppWithShare/SampleChatAppWithShare/FileManager.cpp b/Samples/SampleChatAppWithShare/SampleChatAppWithShare/FileManager.cpp new file mode 100644 index 0000000..4b9014d --- /dev/null +++ b/Samples/SampleChatAppWithShare/SampleChatAppWithShare/FileManager.cpp @@ -0,0 +1,162 @@ +#include "FileManager.h" +#include "ChatModels.h" +#include "ChatManager.h" +#include "UIManager.h" +#include +#include + +#pragma comment(lib, "comdlg32.lib") + +// External declarations for UI window handles +extern HWND hSharedFilesList; + +void ShareFile() +{ + if (selectedContactIndex < 0) { + MessageBox(NULL, L"Please select a contact to share files with. ??", L"No Contact Selected", MB_OK | MB_ICONWARNING); + return; + } + + OPENFILENAME ofn; + WCHAR szFile[260] = {0}; + + ZeroMemory(&ofn, sizeof(ofn)); + ofn.lStructSize = sizeof(ofn); + ofn.lpstrFile = szFile; + ofn.nMaxFile = sizeof(szFile); + ofn.lpstrFilter = L"All Files\0*.*\0?? Text Files\0*.TXT\0?? Document Files\0*.DOC;*.DOCX\0??? Image Files\0*.BMP;*.JPG;*.PNG;*.GIF\0?? PDF Files\0*.PDF\0?? Excel Files\0*.XLS;*.XLSX\0"; + ofn.nFilterIndex = 1; + ofn.lpstrFileTitle = NULL; + ofn.nMaxFileTitle = 0; + ofn.lpstrInitialDir = NULL; + ofn.lpstrTitle = L"Select File to Share"; + ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_EXPLORER; + + if (GetOpenFileName(&ofn)) { + // Extract file name from full path + std::wstring fullPath(szFile); + size_t lastSlash = fullPath.find_last_of(L"\\"); + std::wstring fileName; + if (lastSlash != std::wstring::npos) { + fileName = fullPath.substr(lastSlash + 1); + } else { + fileName = fullPath; + } + + // Create shared file entry + SharedFile newFile; + newFile.fileName = fileName; + newFile.filePath = fullPath; + newFile.sharedBy = L"You"; + GetSystemTime(&newFile.timeShared); + + // Add to chat and shared files + AddSharedFileToChat(newFile, true); + + // Simulate auto-reply from contact acknowledging the file + SetTimer(GetParent(hSharedFilesList), 2, 1500, NULL); + } +} + +void AddSharedFileToChat(const SharedFile& file, bool isOutgoing) +{ + Contact* contact = GetSelectedContact(); + if (!contact) return; + + std::wstring sharer = isOutgoing ? L"You" : contact->name; + std::wstring formattedMessage = sharer + L" shared: " + file.fileName; + + contact->messages.push_back(formattedMessage); + + // Add to shared files list + if (isOutgoing) { + SharedFile newFile = file; + newFile.sharedBy = L"You"; + contact->sharedFiles.push_back(newFile); + } + + // Update both chat display and shared files list + LoadContactChat(selectedContactIndex); +} + +void UpdateSharedFilesList() +{ + Contact* contact = GetSelectedContact(); + if (!contact) return; + + // Clear the list + ::SendMessage(hSharedFilesList, LB_RESETCONTENT, 0, 0); + + // Add shared files with file type icons + for (const auto& file : contact->sharedFiles) { + std::wstring icon = GetFileExtensionIcon(file.fileName); + std::wstring displayText = icon + L" " + file.fileName; + displayText += L"\n ?? " + file.sharedBy; + ::SendMessage(hSharedFilesList, LB_ADDSTRING, 0, (LPARAM)displayText.c_str()); + } +} + +void OpenSharedFile(int fileIndex) +{ + Contact* contact = GetSelectedContact(); + if (!contact || fileIndex < 0 || fileIndex >= (int)contact->sharedFiles.size()) return; + + const SharedFile& file = contact->sharedFiles[fileIndex]; + + // Enhanced file info display with emojis and better formatting + std::wstring message = L"File Information\n\n"; + message += L"?? Name: " + file.fileName + L"\n"; + message += L"?? Path: " + file.filePath + L"\n"; + message += L"?? Shared by: " + file.sharedBy + L"\n"; + + WCHAR timeStr[100]; + swprintf_s(timeStr, 100, L"?? Shared on: %02d/%02d/%04d at %02d:%02d", + file.timeShared.wMonth, file.timeShared.wDay, file.timeShared.wYear, + file.timeShared.wHour, file.timeShared.wMinute); + message += timeStr; + message += L"\n\n?? Tip: In a real application, this file would open automatically!"; + + MessageBox(NULL, message.c_str(), L"Shared File Information", MB_OK | MB_ICONINFORMATION); + + // In a real application, you would use ShellExecute to open the file: + // ShellExecute(NULL, L"open", file.filePath.c_str(), NULL, NULL, SW_SHOWNORMAL); +} + +std::wstring GetFileExtensionIcon(const std::wstring& filePath) +{ + size_t dotPos = filePath.find_last_of(L"."); + if (dotPos != std::wstring::npos) { + std::wstring ext = filePath.substr(dotPos + 1); + std::transform(ext.begin(), ext.end(), ext.begin(), ::towlower); + + if (ext == L"txt") return L"??"; + if (ext == L"doc" || ext == L"docx") return L"??"; + if (ext == L"pdf") return L"??"; + if (ext == L"xls" || ext == L"xlsx") return L"??"; + if (ext == L"jpg" || ext == L"png" || ext == L"gif" || ext == L"bmp") return L"???"; + if (ext == L"mp3" || ext == L"wav") return L"??"; + if (ext == L"mp4" || ext == L"avi") return L"??"; + if (ext == L"zip" || ext == L"rar") return L"???"; + if (ext == L"exe" || ext == L"msi") return L"??"; + } + return L"??"; +} + +std::wstring FormatFileSize(DWORD fileSize) +{ + const DWORD KB = 1024; + const DWORD MB = KB * 1024; + const DWORD GB = MB * 1024; + + WCHAR buffer[50]; + if (fileSize >= GB) { + swprintf_s(buffer, 50, L"%.1f GB", (double)fileSize / GB); + } else if (fileSize >= MB) { + swprintf_s(buffer, 50, L"%.1f MB", (double)fileSize / MB); + } else if (fileSize >= KB) { + swprintf_s(buffer, 50, L"%.1f KB", (double)fileSize / KB); + } else { + swprintf_s(buffer, 50, L"%d bytes", fileSize); + } + return std::wstring(buffer); +} \ No newline at end of file diff --git a/Samples/SampleChatAppWithShare/SampleChatAppWithShare/FileManager.h b/Samples/SampleChatAppWithShare/SampleChatAppWithShare/FileManager.h new file mode 100644 index 0000000..e28e4db --- /dev/null +++ b/Samples/SampleChatAppWithShare/SampleChatAppWithShare/FileManager.h @@ -0,0 +1,13 @@ +#pragma once + +#include +#include +#include "ChatModels.h" + +// File management functions +void ShareFile(); +void AddSharedFileToChat(const SharedFile& file, bool isOutgoing); +void UpdateSharedFilesList(); +void OpenSharedFile(int fileIndex); +std::wstring GetFileExtensionIcon(const std::wstring& filePath); +std::wstring FormatFileSize(DWORD fileSize); \ No newline at end of file diff --git a/Samples/SampleChatAppWithShare/SampleChatAppWithShare/ModernUI.cpp b/Samples/SampleChatAppWithShare/SampleChatAppWithShare/ModernUI.cpp new file mode 100644 index 0000000..ca30eb2 --- /dev/null +++ b/Samples/SampleChatAppWithShare/SampleChatAppWithShare/ModernUI.cpp @@ -0,0 +1,128 @@ +#include "ModernUI.h" +#include "UIConstants.h" +#include + +#pragma comment(lib, "gdiplus.lib") + +// Modern UI Variables definitions +HBRUSH hBrushBackground = nullptr; +HBRUSH hBrushSurface = nullptr; +HBRUSH hBrushPrimary = nullptr; +HBRUSH hBrushHover = nullptr; +HFONT hFontRegular = nullptr; +HFONT hFontBold = nullptr; +HFONT hFontTitle = nullptr; +HPEN hPenBorder = nullptr; + +void InitializeModernUI() +{ + // Create brushes for modern color scheme + hBrushBackground = CreateSolidBrush(COLOR_APP_BACKGROUND); + hBrushSurface = CreateSolidBrush(COLOR_SURFACE); + hBrushPrimary = CreateSolidBrush(COLOR_PRIMARY); + hBrushHover = CreateSolidBrush(COLOR_HOVER); + + // Create modern fonts + hFontRegular = CreateFont(16, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, + DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, + CLEARTYPE_QUALITY, DEFAULT_PITCH | FF_DONTCARE, L"Segoe UI"); + + hFontBold = CreateFont(16, 0, 0, 0, FW_SEMIBOLD, FALSE, FALSE, FALSE, + DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, + CLEARTYPE_QUALITY, DEFAULT_PITCH | FF_DONTCARE, L"Segoe UI"); + + hFontTitle = CreateFont(20, 0, 0, 0, FW_SEMIBOLD, FALSE, FALSE, FALSE, + DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, + CLEARTYPE_QUALITY, DEFAULT_PITCH | FF_DONTCARE, L"Segoe UI"); + + // Create pen for borders + hPenBorder = CreatePen(PS_SOLID, 1, COLOR_BORDER); + + // Initialize GDI+ + Gdiplus::GdiplusStartupInput gdiplusStartupInput; + ULONG_PTR gdiplusToken; + Gdiplus::GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL); +} + +void CleanupModernUI() +{ + // Cleanup GDI objects + if (hBrushBackground) DeleteObject(hBrushBackground); + if (hBrushSurface) DeleteObject(hBrushSurface); + if (hBrushPrimary) DeleteObject(hBrushPrimary); + if (hBrushHover) DeleteObject(hBrushHover); + if (hFontRegular) DeleteObject(hFontRegular); + if (hFontBold) DeleteObject(hFontBold); + if (hFontTitle) DeleteObject(hFontTitle); + if (hPenBorder) DeleteObject(hPenBorder); + + // Shutdown GDI+ + Gdiplus::GdiplusShutdown(NULL); +} + +void DrawModernButton(HDC hdc, RECT rect, const std::wstring& text, bool isHovered, bool isPressed) +{ + // Create rounded rectangle region + HRGN hRgn = CreateRoundRectRgn(rect.left, rect.top, rect.right, rect.bottom, 8, 8); + + // Fill background + HBRUSH hBrush = CreateSolidBrush(isPressed ? COLOR_PRIMARY_DARK : + isHovered ? COLOR_PRIMARY : COLOR_PRIMARY); + FillRgn(hdc, hRgn, hBrush); + DeleteObject(hBrush); + + // Draw border with a brush instead of pen + HBRUSH borderBrush = CreateSolidBrush(COLOR_BORDER); + FrameRgn(hdc, hRgn, borderBrush, 1, 1); + DeleteObject(borderBrush); + DeleteObject(hRgn); + + // Draw text + SetBkMode(hdc, TRANSPARENT); + SetTextColor(hdc, RGB(255, 255, 255)); + SelectObject(hdc, hFontBold); + + DrawText(hdc, text.c_str(), -1, &rect, DT_CENTER | DT_VCENTER | DT_SINGLELINE); +} + +void DrawContactItem(HDC hdc, RECT rect, const Contact& contact, bool isSelected) +{ + // Fill background + HBRUSH bgBrush = CreateSolidBrush(isSelected ? COLOR_HOVER : COLOR_SURFACE); + FillRect(hdc, &rect, bgBrush); + DeleteObject(bgBrush); + + // Draw avatar circle + int avatarX = rect.left + 12; + int avatarY = rect.top + (rect.bottom - rect.top - AVATAR_SIZE) / 2; + + HBRUSH avatarBrush = CreateSolidBrush(COLOR_PRIMARY); + HPEN avatarPen = CreatePen(PS_SOLID, 2, contact.isOnline ? RGB(34, 197, 94) : RGB(156, 163, 175)); + + SelectObject(hdc, avatarBrush); + SelectObject(hdc, avatarPen); + + Ellipse(hdc, avatarX, avatarY, avatarX + AVATAR_SIZE, avatarY + AVATAR_SIZE); + + DeleteObject(avatarBrush); + DeleteObject(avatarPen); + + // Draw contact name + SetBkMode(hdc, TRANSPARENT); + SetTextColor(hdc, COLOR_TEXT_PRIMARY); + SelectObject(hdc, hFontBold); + + RECT nameRect = {avatarX + AVATAR_SIZE + 12, rect.top + 8, rect.right - 8, rect.top + 28}; + DrawText(hdc, contact.name.c_str(), -1, &nameRect, DT_LEFT | DT_VCENTER | DT_SINGLELINE); + + // Draw status + SetTextColor(hdc, COLOR_TEXT_SECONDARY); + SelectObject(hdc, hFontRegular); + + RECT statusRect = {avatarX + AVATAR_SIZE + 12, rect.top + 30, rect.right - 8, rect.top + 48}; + DrawText(hdc, contact.status.c_str(), -1, &statusRect, DT_LEFT | DT_VCENTER | DT_SINGLELINE); + + // Draw last message preview + RECT msgRect = {avatarX + AVATAR_SIZE + 12, rect.top + 50, rect.right - 8, rect.bottom - 8}; + DrawText(hdc, contact.lastMessage.c_str(), -1, &msgRect, DT_LEFT | DT_VCENTER | DT_SINGLELINE | DT_END_ELLIPSIS); +} \ No newline at end of file diff --git a/Samples/SampleChatAppWithShare/SampleChatAppWithShare/ModernUI.h b/Samples/SampleChatAppWithShare/SampleChatAppWithShare/ModernUI.h new file mode 100644 index 0000000..3cb9ea7 --- /dev/null +++ b/Samples/SampleChatAppWithShare/SampleChatAppWithShare/ModernUI.h @@ -0,0 +1,21 @@ +#pragma once + +#include +#include +#include "ChatModels.h" + +// Modern UI Variables +extern HBRUSH hBrushBackground; +extern HBRUSH hBrushSurface; +extern HBRUSH hBrushPrimary; +extern HBRUSH hBrushHover; +extern HFONT hFontRegular; +extern HFONT hFontBold; +extern HFONT hFontTitle; +extern HPEN hPenBorder; + +// Modern UI Functions +void InitializeModernUI(); +void CleanupModernUI(); +void DrawModernButton(HDC hdc, RECT rect, const std::wstring& text, bool isHovered, bool isPressed); +void DrawContactItem(HDC hdc, RECT rect, const Contact& contact, bool isSelected); \ No newline at end of file diff --git a/Samples/SampleChatAppWithShare/SampleChatAppWithShare/SampleChatAppWithShare.cpp b/Samples/SampleChatAppWithShare/SampleChatAppWithShare/SampleChatAppWithShare.cpp index 4abaee2..c1e4396 100644 --- a/Samples/SampleChatAppWithShare/SampleChatAppWithShare/SampleChatAppWithShare.cpp +++ b/Samples/SampleChatAppWithShare/SampleChatAppWithShare/SampleChatAppWithShare.cpp @@ -3,106 +3,32 @@ #include "framework.h" #include "SampleChatAppWithShare.h" -#include -#include -#include -#include +#include "UIConstants.h" +#include "ChatModels.h" +#include "ModernUI.h" +#include "ChatManager.h" +#include "FileManager.h" +#include "UIManager.h" +#include "WindowProcs.h" #include -#include #include -#include -#include +#include #pragma comment(lib, "comctl32.lib") -#pragma comment(lib, "comdlg32.lib") #pragma comment(lib, "shell32.lib") -#pragma comment(lib, "gdiplus.lib") #pragma comment(lib, "uxtheme.lib") - -#define MAX_LOADSTRING 100 - -// Modern UI Color Scheme -#define COLOR_PRIMARY RGB(64, 128, 255) // Modern blue -#define COLOR_PRIMARY_DARK RGB(45, 100, 220) // Darker blue for hover -#define COLOR_APP_BACKGROUND RGB(248, 249, 250) // Light gray background -#define COLOR_SURFACE RGB(255, 255, 255) // White surface -#define COLOR_TEXT_PRIMARY RGB(33, 37, 41) // Dark text -#define COLOR_TEXT_SECONDARY RGB(108, 117, 125) // Gray text -#define COLOR_BORDER RGB(222, 226, 230) // Light border -#define COLOR_HOVER RGB(248, 249, 250) // Hover background -#define COLOR_CHAT_BUBBLE_OUT RGB(0, 123, 255) // Outgoing message -#define COLOR_CHAT_BUBBLE_IN RGB(233, 236, 239) // Incoming message +#pragma comment(lib, "ole32.lib") // Global Variables: HINSTANCE hInst; // current instance WCHAR szTitle[MAX_LOADSTRING]; // The title bar text WCHAR szWindowClass[MAX_LOADSTRING]; // the main window class name -// Chat Application Variables -HWND hContactsList; -HWND hChatDisplay; -HWND hMessageInput; -HWND hSendButton; -HWND hContactName; -HWND hShareFileButton; -HWND hSharedFilesList; - -// Modern UI Variables -HBRUSH hBrushBackground; -HBRUSH hBrushSurface; -HBRUSH hBrushPrimary; -HBRUSH hBrushHover; -HFONT hFontRegular; -HFONT hFontBold; -HFONT hFontTitle; -HPEN hPenBorder; - -struct SharedFile { - std::wstring fileName; - std::wstring filePath; - std::wstring sharedBy; - SYSTEMTIME timeShared; -}; - -struct Contact { - std::wstring name; - std::wstring lastMessage; - std::vector messages; - std::vector sharedFiles; - std::wstring status; - bool isOnline; -}; - -std::vector contacts; -std::map> chatHistory; -int selectedContactIndex = -1; - // Forward declarations of functions included in this code module: ATOM MyRegisterClass(HINSTANCE hInstance); BOOL InitInstance(HINSTANCE, int); LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); -LRESULT CALLBACK ModernButtonProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); -LRESULT CALLBACK ModernListBoxProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); INT_PTR CALLBACK About(HWND, UINT, WPARAM, LPARAM); -void InitializeContacts(); -void CreateChatUI(HWND hWnd); -void LoadContactChat(int contactIndex); -void SendChatMessage(); -void AddMessageToChat(const std::wstring& message, bool isOutgoing); -void ShareFile(); -void AddSharedFileToChat(const SharedFile& file, bool isOutgoing); -void UpdateSharedFilesList(); -void OpenSharedFile(int fileIndex); -std::wstring GetFileExtensionIcon(const std::wstring& filePath); -std::wstring FormatFileSize(DWORD fileSize); -void InitializeModernUI(); -void CleanupModernUI(); -void DrawModernButton(HDC hdc, RECT rect, const std::wstring& text, bool isHovered, bool isPressed); -void DrawContactItem(HDC hdc, RECT rect, const Contact& contact, bool isSelected); - -// Window procedure storage for subclassing -WNDPROC originalButtonProc = nullptr; -WNDPROC originalListBoxProc = nullptr; int APIENTRY wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, @@ -210,582 +136,11 @@ BOOL InitInstance(HINSTANCE hInstance, int nCmdShow) return TRUE; } -void InitializeContacts() -{ - contacts = { - {L"Alice Johnson", L"Hey, how are you?", {L"Alice: Hey, how are you?", L"You: I'm doing great, thanks!", L"Alice: That's wonderful to hear!"}, {}, L"Available", true}, - {L"Bob Smith", L"See you tomorrow!", {L"Bob: Are we still meeting tomorrow?", L"You: Yes, see you at 3 PM", L"Bob: See you tomorrow!"}, {}, L"In a meeting", true}, - {L"Carol Williams", L"Thanks for the help", {L"Carol: Could you help me with the project?", L"You: Of course! What do you need?", L"Carol: Thanks for the help"}, {}, L"Available", true}, - {L"David Brown", L"Great presentation!", {L"David: Great presentation today!", L"You: Thank you! I'm glad you liked it"}, {}, L"Away", false}, - {L"Emma Davis", L"Coffee later?", {L"Emma: Want to grab coffee later?", L"You: Sure! What time works for you?", L"Emma: Coffee later?"}, {}, L"Available", true}, - {L"Frank Miller", L"Happy Birthday!", {L"Frank: Happy Birthday!", L"You: Thank you so much!"}, {}, L"Busy", true}, - {L"Grace Wilson", L"Meeting rescheduled", {L"Grace: Meeting has been rescheduled to 4 PM", L"You: Got it, thanks for letting me know"}, {}, L"Available", true}, - {L"Henry Taylor", L"Weekend plans?", {L"Henry: Any plans for the weekend?", L"You: Nothing concrete yet", L"Henry: Weekend plans?"}, {}, L"Offline", false}, - {L"Ivy Anderson", L"Project update", {L"Ivy: Here's the project update you requested", L"You: Perfect, reviewing it now"}, {}, L"Available", true}, - {L"Jack Thompson", L"Game night Friday", {L"Jack: Game night this Friday?", L"You: Count me in!", L"Jack: Game night Friday"}, {}, L"Gaming", true}, - {L"Kate Garcia", L"Recipe sharing", {L"Kate: Loved that recipe you shared!", L"You: I'm so glad you enjoyed it!"}, {}, L"Cooking", true}, - {L"Leo Martinez", L"Workout buddy", {L"Leo: Gym session tomorrow morning?", L"You: Absolutely! 7 AM as usual?"}, {}, L"At the gym", true}, - {L"Mia Rodriguez", L"Book recommendation", {L"Mia: Any good book recommendations?", L"You: I just finished a great mystery novel"}, {}, L"Reading", true}, - {L"Noah Lee", L"Tech discussion", {L"Noah: Thoughts on the new framework?", L"You: It looks promising! Want to discuss over lunch?"}, {}, L"Coding", true}, - {L"Olivia Clark", L"Travel planning", {L"Olivia: Planning the vacation itinerary", L"You: Excited to see what you've planned!"}, {}, L"Traveling", false} - }; - - // Add some sample shared files to demonstrate the feature - SYSTEMTIME st; - GetSystemTime(&st); - - contacts[0].sharedFiles.push_back({L"Project_Proposal.docx", L"C:\\Documents\\Project_Proposal.docx", L"Alice", st}); - contacts[1].sharedFiles.push_back({L"Meeting_Notes.pdf", L"C:\\Documents\\Meeting_Notes.pdf", L"Bob", st}); - contacts[2].sharedFiles.push_back({L"Budget_Spreadsheet.xlsx", L"C:\\Documents\\Budget_Spreadsheet.xlsx", L"Carol", st}); -} - -void InitializeModernUI() -{ - // Create brushes for modern color scheme - hBrushBackground = CreateSolidBrush(COLOR_APP_BACKGROUND); - hBrushSurface = CreateSolidBrush(COLOR_SURFACE); - hBrushPrimary = CreateSolidBrush(COLOR_PRIMARY); - hBrushHover = CreateSolidBrush(COLOR_HOVER); - - // Create modern fonts - hFontRegular = CreateFont(16, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, - DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, - CLEARTYPE_QUALITY, DEFAULT_PITCH | FF_DONTCARE, L"Segoe UI"); - - hFontBold = CreateFont(16, 0, 0, 0, FW_SEMIBOLD, FALSE, FALSE, FALSE, - DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, - CLEARTYPE_QUALITY, DEFAULT_PITCH | FF_DONTCARE, L"Segoe UI"); - - hFontTitle = CreateFont(20, 0, 0, 0, FW_SEMIBOLD, FALSE, FALSE, FALSE, - DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, - CLEARTYPE_QUALITY, DEFAULT_PITCH | FF_DONTCARE, L"Segoe UI"); - - // Create pen for borders - hPenBorder = CreatePen(PS_SOLID, 1, COLOR_BORDER); - - // Initialize GDI+ - Gdiplus::GdiplusStartupInput gdiplusStartupInput; - ULONG_PTR gdiplusToken; - Gdiplus::GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL); -} - -void CleanupModernUI() -{ - // Cleanup GDI objects - if (hBrushBackground) DeleteObject(hBrushBackground); - if (hBrushSurface) DeleteObject(hBrushSurface); - if (hBrushPrimary) DeleteObject(hBrushPrimary); - if (hBrushHover) DeleteObject(hBrushHover); - if (hFontRegular) DeleteObject(hFontRegular); - if (hFontBold) DeleteObject(hFontBold); - if (hFontTitle) DeleteObject(hFontTitle); - if (hPenBorder) DeleteObject(hPenBorder); - - // Shutdown GDI+ - Gdiplus::GdiplusShutdown(NULL); -} - -void DrawModernButton(HDC hdc, RECT rect, const std::wstring& text, bool isHovered, bool isPressed) -{ - // Create rounded rectangle region - HRGN hRgn = CreateRoundRectRgn(rect.left, rect.top, rect.right, rect.bottom, 8, 8); - - // Fill background - HBRUSH hBrush = CreateSolidBrush(isPressed ? COLOR_PRIMARY_DARK : - isHovered ? COLOR_PRIMARY : COLOR_PRIMARY); - FillRgn(hdc, hRgn, hBrush); - DeleteObject(hBrush); - - // Draw border with a brush instead of pen - HBRUSH borderBrush = CreateSolidBrush(COLOR_BORDER); - FrameRgn(hdc, hRgn, borderBrush, 1, 1); - DeleteObject(borderBrush); - DeleteObject(hRgn); - - // Draw text - SetBkMode(hdc, TRANSPARENT); - SetTextColor(hdc, RGB(255, 255, 255)); - SelectObject(hdc, hFontBold); - - DrawText(hdc, text.c_str(), -1, &rect, DT_CENTER | DT_VCENTER | DT_SINGLELINE); -} - -void DrawContactItem(HDC hdc, RECT rect, const Contact& contact, bool isSelected) -{ - // Fill background - HBRUSH bgBrush = CreateSolidBrush(isSelected ? COLOR_HOVER : COLOR_SURFACE); - FillRect(hdc, &rect, bgBrush); - DeleteObject(bgBrush); - - // Draw avatar circle - int avatarSize = 40; - int avatarX = rect.left + 12; - int avatarY = rect.top + (rect.bottom - rect.top - avatarSize) / 2; - - HBRUSH avatarBrush = CreateSolidBrush(COLOR_PRIMARY); - HPEN avatarPen = CreatePen(PS_SOLID, 2, contact.isOnline ? RGB(34, 197, 94) : RGB(156, 163, 175)); - - SelectObject(hdc, avatarBrush); - SelectObject(hdc, avatarPen); - - Ellipse(hdc, avatarX, avatarY, avatarX + avatarSize, avatarY + avatarSize); - - DeleteObject(avatarBrush); - DeleteObject(avatarPen); - - // Draw contact name - SetBkMode(hdc, TRANSPARENT); - SetTextColor(hdc, COLOR_TEXT_PRIMARY); - SelectObject(hdc, hFontBold); - - RECT nameRect = {avatarX + avatarSize + 12, rect.top + 8, rect.right - 8, rect.top + 28}; - DrawText(hdc, contact.name.c_str(), -1, &nameRect, DT_LEFT | DT_VCENTER | DT_SINGLELINE); - - // Draw status - SetTextColor(hdc, COLOR_TEXT_SECONDARY); - SelectObject(hdc, hFontRegular); - - RECT statusRect = {avatarX + avatarSize + 12, rect.top + 30, rect.right - 8, rect.top + 48}; - DrawText(hdc, contact.status.c_str(), -1, &statusRect, DT_LEFT | DT_VCENTER | DT_SINGLELINE); - - // Draw last message preview - RECT msgRect = {avatarX + avatarSize + 12, rect.top + 50, rect.right - 8, rect.bottom - 8}; - DrawText(hdc, contact.lastMessage.c_str(), -1, &msgRect, DT_LEFT | DT_VCENTER | DT_SINGLELINE | DT_END_ELLIPSIS); -} - -void CreateChatUI(HWND hWnd) -{ - RECT rect; - GetClientRect(hWnd, &rect); - - int width = rect.right - rect.left; - int height = rect.bottom - rect.top; - - // Set window background to modern color - SetClassLongPtr(hWnd, GCLP_HBRBACKGROUND, (LONG_PTR)hBrushBackground); - - // Create contacts list (left panel) with modern styling - hContactsList = CreateWindowEx( - WS_EX_CLIENTEDGE, - L"LISTBOX", NULL, - WS_CHILD | WS_VISIBLE | WS_VSCROLL | LBS_NOTIFY | LBS_OWNERDRAWFIXED, - 20, 20, 280, height - 40, - hWnd, (HMENU)IDC_CONTACTS_LIST, hInst, NULL); - - // Set custom item height for contact list - ::SendMessage(hContactsList, LB_SETITEMHEIGHT, 0, 72); - - // Create contact name label with modern styling - hContactName = CreateWindow(L"STATIC", L"Select a contact to start chatting", - WS_CHILD | WS_VISIBLE | SS_LEFT, - 320, 20, 300, 30, - hWnd, (HMENU)IDC_CONTACT_NAME, hInst, NULL); - - // Create chat display area with modern styling - hChatDisplay = CreateWindowEx( - WS_EX_CLIENTEDGE, - L"EDIT", NULL, - WS_CHILD | WS_VISIBLE | WS_VSCROLL | ES_MULTILINE | ES_READONLY | ES_AUTOVSCROLL, - 320, 60, width - 600, height - 280, - hWnd, (HMENU)IDC_CHAT_DISPLAY, hInst, NULL); - - // Create shared files section header - CreateWindow(L"STATIC", L"Shared Files", - WS_CHILD | WS_VISIBLE | SS_LEFT, - width - 260, 20, 120, 30, - hWnd, NULL, hInst, NULL); - - // Create shared files list with modern styling - hSharedFilesList = CreateWindowEx( - WS_EX_CLIENTEDGE, - L"LISTBOX", NULL, - WS_CHILD | WS_VISIBLE | WS_VSCROLL | LBS_NOTIFY, - width - 260, 60, 240, height - 280, - hWnd, (HMENU)IDC_SHARED_FILES_LIST, hInst, NULL); - - // Create message input with placeholder styling - hMessageInput = CreateWindowEx( - WS_EX_CLIENTEDGE, - L"EDIT", NULL, - WS_CHILD | WS_VISIBLE | ES_MULTILINE | ES_AUTOVSCROLL, - 320, height - 200, width - 600, 60, - hWnd, (HMENU)IDC_MESSAGE_INPUT, hInst, NULL); - - // Create modern styled buttons - hSendButton = CreateWindow(L"BUTTON", L"Send", - WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | BS_OWNERDRAW, - 320, height - 130, 100, 45, - hWnd, (HMENU)IDC_SEND_BUTTON, hInst, NULL); - - hShareFileButton = CreateWindow(L"BUTTON", L"Share File", - WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | BS_OWNERDRAW, - 440, height - 130, 120, 45, - hWnd, (HMENU)IDC_SHARE_FILE_BUTTON, hInst, NULL); - - // Populate contacts list - for (const auto& contact : contacts) { - ::SendMessage(hContactsList, LB_ADDSTRING, 0, (LPARAM)contact.name.c_str()); - } - - // Apply modern fonts to controls - ::SendMessage(hContactName, WM_SETFONT, (WPARAM)hFontTitle, TRUE); - ::SendMessage(hChatDisplay, WM_SETFONT, (WPARAM)hFontRegular, TRUE); - ::SendMessage(hMessageInput, WM_SETFONT, (WPARAM)hFontRegular, TRUE); - ::SendMessage(hSharedFilesList, WM_SETFONT, (WPARAM)hFontRegular, TRUE); - - // Subclass buttons for custom drawing - originalButtonProc = (WNDPROC)SetWindowLongPtr(hSendButton, GWLP_WNDPROC, (LONG_PTR)ModernButtonProc); - SetWindowLongPtr(hShareFileButton, GWLP_WNDPROC, (LONG_PTR)ModernButtonProc); - - // Subclass contact list for custom drawing - originalListBoxProc = (WNDPROC)SetWindowLongPtr(hContactsList, GWLP_WNDPROC, (LONG_PTR)ModernListBoxProc); - - // Set modern background colors - SetClassLongPtr(hChatDisplay, GCLP_HBRBACKGROUND, (LONG_PTR)hBrushSurface); - SetClassLongPtr(hMessageInput, GCLP_HBRBACKGROUND, (LONG_PTR)hBrushSurface); - SetClassLongPtr(hSharedFilesList, GCLP_HBRBACKGROUND, (LONG_PTR)hBrushSurface); -} - -void LoadContactChat(int contactIndex) -{ - if (contactIndex < 0 || contactIndex >= (int)contacts.size()) return; - - selectedContactIndex = contactIndex; - const Contact& contact = contacts[contactIndex]; - - // Update contact name with status indicator - std::wstring headerText = contact.name + L" " + L" (" + contact.status + L")"; - SetWindowText(hContactName, headerText.c_str()); - - // Clear and populate chat display with better formatting - SetWindowText(hChatDisplay, L""); - - std::wstring chatText; - for (const auto& message : contact.messages) { - // Add timestamps and better message formatting - SYSTEMTIME st; - GetLocalTime(&st); - WCHAR timeStr[50]; - swprintf_s(timeStr, 50, L"[%02d:%02d] ", st.wHour, st.wMinute); - - if (message.find(L"You:") == 0) { - chatText += L" "; // Right align for your messages - chatText += timeStr + message + L"\r\n\r\n"; - } else { - chatText += timeStr + message + L"\r\n\r\n"; - } - } - - SetWindowText(hChatDisplay, chatText.c_str()); - - // Scroll to bottom - ::SendMessage(hChatDisplay, EM_SETSEL, -1, -1); - ::SendMessage(hChatDisplay, EM_SCROLLCARET, 0, 0); - - // Update shared files list - UpdateSharedFilesList(); - - // Refresh contact list to show selection - InvalidateRect(hContactsList, NULL, TRUE); -} - -void UpdateSharedFilesList() -{ - if (selectedContactIndex < 0 || selectedContactIndex >= (int)contacts.size()) return; - - const Contact& contact = contacts[selectedContactIndex]; - - // Clear the list - ::SendMessage(hSharedFilesList, LB_RESETCONTENT, 0, 0); - - // Add shared files with file type icons - for (const auto& file : contact.sharedFiles) { - std::wstring icon = GetFileExtensionIcon(file.fileName); - std::wstring displayText = icon + L" " + file.fileName; - displayText += L"\n ?? " + file.sharedBy; - ::SendMessage(hSharedFilesList, LB_ADDSTRING, 0, (LPARAM)displayText.c_str()); - } -} - -void AddMessageToChat(const std::wstring& message, bool isOutgoing) -{ - if (selectedContactIndex < 0 || selectedContactIndex >= (int)contacts.size()) return; - - Contact& contact = contacts[selectedContactIndex]; - - // Add timestamp to message - SYSTEMTIME st; - GetLocalTime(&st); - WCHAR timeStr[50]; - swprintf_s(timeStr, 50, L"[%02d:%02d] ", st.wHour, st.wMinute); - - std::wstring formattedMessage; - if (isOutgoing) { - formattedMessage = L"You: " + message + L" ?"; - } else { - formattedMessage = contact.name + L": " + message; - } - - contact.messages.push_back(formattedMessage); - contact.lastMessage = message.length() > 50 ? message.substr(0, 47) + L"..." : message; - - // Update chat display - LoadContactChat(selectedContactIndex); - - // Refresh contacts list to update last message preview - InvalidateRect(hContactsList, NULL, TRUE); -} - -void AddSharedFileToChat(const SharedFile& file, bool isOutgoing) -{ - if (selectedContactIndex < 0 || selectedContactIndex >= (int)contacts.size()) return; - - Contact& contact = contacts[selectedContactIndex]; - std::wstring sharer = isOutgoing ? L"You" : contact.name; - std::wstring formattedMessage = sharer + L" shared: " + file.fileName; - - contact.messages.push_back(formattedMessage); - - // Add to shared files list - if (isOutgoing) { - SharedFile newFile = file; - newFile.sharedBy = L"You"; - contact.sharedFiles.push_back(newFile); - } - - // Update both chat display and shared files list - LoadContactChat(selectedContactIndex); -} - -void ShareFile() -{ - if (selectedContactIndex < 0) { - MessageBox(NULL, L"Please select a contact to share files with. ??", L"No Contact Selected", MB_OK | MB_ICONWARNING); - return; - } - - OPENFILENAME ofn; - WCHAR szFile[260] = {0}; - - ZeroMemory(&ofn, sizeof(ofn)); - ofn.lStructSize = sizeof(ofn); - ofn.lpstrFile = szFile; - ofn.nMaxFile = sizeof(szFile); - ofn.lpstrFilter = L"All Files\0*.*\0?? Text Files\0*.TXT\0?? Document Files\0*.DOC;*.DOCX\0??? Image Files\0*.BMP;*.JPG;*.PNG;*.GIF\0?? PDF Files\0*.PDF\0?? Excel Files\0*.XLS;*.XLSX\0"; - ofn.nFilterIndex = 1; - ofn.lpstrFileTitle = NULL; - ofn.nMaxFileTitle = 0; - ofn.lpstrInitialDir = NULL; - ofn.lpstrTitle = L"Select File to Share"; - ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_EXPLORER; - - if (GetOpenFileName(&ofn)) { - // Extract file name from full path - std::wstring fullPath(szFile); - size_t lastSlash = fullPath.find_last_of(L"\\");// Create shared file entry - std::wstring fileName; - if (lastSlash != std::wstring::npos) { - fileName = fullPath.substr(lastSlash + 1); - } else { - fileName = fullPath; - } - - // Create shared file entry - SharedFile newFile; - newFile.fileName = fileName; - newFile.filePath = fullPath; - newFile.sharedBy = L"You"; - GetSystemTime(&newFile.timeShared); - - // Add to chat and shared files - AddSharedFileToChat(newFile, true); - - // Simulate auto-reply from contact acknowledging the file - SetTimer(GetParent(hMessageInput), 2, 1500, NULL); - } -} - -void OpenSharedFile(int fileIndex) -{ - if (selectedContactIndex < 0 || selectedContactIndex >= (int)contacts.size()) return; - - const Contact& contact = contacts[selectedContactIndex]; - if (fileIndex < 0 || fileIndex >= (int)contact.sharedFiles.size()) return; - - const SharedFile& file = contact.sharedFiles[fileIndex]; - - // Enhanced file info display with emojis and better formatting - std::wstring message = L"File Information\n\n"; - message += L"Name: " + file.fileName + L"\n"; - message += L"Path: " + file.filePath + L"\n"; - message += L"Shared by: " + file.sharedBy + L"\n"; - - WCHAR timeStr[100]; - swprintf_s(timeStr, 100, L"Shared on: %02d/%02d/%04d at %02d:%02d", - file.timeShared.wMonth, file.timeShared.wDay, file.timeShared.wYear, - file.timeShared.wHour, file.timeShared.wMinute); - message += timeStr; - message += L"\n\nTip: In a real application, this file would open automatically!"; - - MessageBox(NULL, message.c_str(), L"Shared File Information", MB_OK | MB_ICONINFORMATION); - - // In a real application, you would use ShellExecute to open the file: - // ShellExecute(NULL, L"open", file.filePath.c_str(), NULL, NULL, SW_SHOWNORMAL); -} - -std::wstring GetFileExtensionIcon(const std::wstring& filePath) -{ - size_t dotPos = filePath.find_last_of(L"."); - if (dotPos != std::wstring::npos) { - std::wstring ext = filePath.substr(dotPos + 1); - std::transform(ext.begin(), ext.end(), ext.begin(), ::towlower); - - if (ext == L"txt") return L"??"; - if (ext == L"doc" || ext == L"docx") return L"??"; - if (ext == L"pdf") return L"??"; - if (ext == L"xls" || ext == L"xlsx") return L"??"; - if (ext == L"jpg" || ext == L"png" || ext == L"gif" || ext == L"bmp") return L"???"; - if (ext == L"mp3" || ext == L"wav") return L"??"; - if (ext == L"mp4" || ext == L"avi") return L"??"; - if (ext == L"zip" || ext == L"rar") return L"???"; - if (ext == L"exe" || ext == L"msi") return L"??"; - } - return L"??"; -} - -std::wstring FormatFileSize(DWORD fileSize) -{ - const DWORD KB = 1024; - const DWORD MB = KB * 1024; - const DWORD GB = MB * 1024; - - WCHAR buffer[50]; - if (fileSize >= GB) { - swprintf_s(buffer, 50, L"%.1f GB", (double)fileSize / GB); - } else if (fileSize >= MB) { - swprintf_s(buffer, 50, L"%.1f MB", (double)fileSize / MB); - } else if (fileSize >= KB) { - swprintf_s(buffer, 50, L"%.1f KB", (double)fileSize / KB); - } else { - swprintf_s(buffer, 50, L"%d bytes", fileSize); - } - return std::wstring(buffer); -} - -// Modern button subclass procedure -LRESULT CALLBACK ModernButtonProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) -{ - static bool isHovered = false; - static bool isPressed = false; - - switch (msg) - { - case WM_PAINT: - { - PAINTSTRUCT ps; - HDC hdc = BeginPaint(hWnd, &ps); - - RECT rect; - GetClientRect(hWnd, &rect); - - WCHAR text[256]; - GetWindowText(hWnd, text, 256); - - DrawModernButton(hdc, rect, std::wstring(text), isHovered, isPressed); - - EndPaint(hWnd, &ps); - return 0; - } - - case WM_MOUSEMOVE: - if (!isHovered) - { - isHovered = true; - InvalidateRect(hWnd, NULL, FALSE); - - TRACKMOUSEEVENT tme = {}; - tme.cbSize = sizeof(tme); - tme.dwFlags = TME_LEAVE; - tme.hwndTrack = hWnd; - TrackMouseEvent(&tme); - } - break; - - case WM_MOUSELEAVE: - isHovered = false; - InvalidateRect(hWnd, NULL, FALSE); - break; - - case WM_LBUTTONDOWN: - isPressed = true; - InvalidateRect(hWnd, NULL, FALSE); - SetCapture(hWnd); - break; - - case WM_LBUTTONUP: - if (isPressed) - { - isPressed = false; - InvalidateRect(hWnd, NULL, FALSE); - ReleaseCapture(); - - // Send click notification to parent - HWND hParent = GetParent(hWnd); - int controlId = GetDlgCtrlID(hWnd); - ::SendMessage(hParent, WM_COMMAND, MAKEWPARAM(controlId, BN_CLICKED), (LPARAM)hWnd); - } - break; - } - - return CallWindowProc(originalButtonProc, hWnd, msg, wParam, lParam); -} - -// Modern listbox subclass procedure -LRESULT CALLBACK ModernListBoxProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) -{ - switch (msg) - { - case WM_PAINT: - if (hWnd == hContactsList) - { - PAINTSTRUCT ps; - HDC hdc = BeginPaint(hWnd, &ps); - - RECT clientRect; - GetClientRect(hWnd, &clientRect); - - // Fill background - FillRect(hdc, &clientRect, hBrushSurface); - - int itemCount = contacts.size(); - int itemHeight = 72; - int selectedIndex = ::SendMessage(hWnd, LB_GETCURSEL, 0, 0); - - for (int i = 0; i < itemCount; i++) - { - RECT itemRect = {0, i * itemHeight, clientRect.right, (i + 1) * itemHeight}; - DrawContactItem(hdc, itemRect, contacts[i], i == selectedIndex); - } - - EndPaint(hWnd, &ps); - return 0; - } - break; - } - - return CallWindowProc(originalListBoxProc, hWnd, msg, wParam, lParam); -} - // // FUNCTION: WndProc(HWND, UINT, WPARAM, LPARAM) // // PURPOSE: Processes messages for the main window. // -// WM_COMMAND - process the application menu -// WM_PAINT - Paint the main window -// WM_DESTROY - post a quit message and return -// -// LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { switch (message) @@ -795,32 +150,7 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) break; case WM_SIZE: - { - // Resize chat UI elements when window is resized with modern spacing - RECT rect; - GetClientRect(hWnd, &rect); - int width = rect.right - rect.left; - int height = rect.bottom - rect.top; - - if (hContactsList) { - SetWindowPos(hContactsList, NULL, 20, 20, 280, height - 40, SWP_NOZORDER); - } - if (hChatDisplay) { - SetWindowPos(hChatDisplay, NULL, 320, 60, width - 600, height - 280, SWP_NOZORDER); - } - if (hSharedFilesList) { - SetWindowPos(hSharedFilesList, NULL, width - 260, 60, 240, height - 280, SWP_NOZORDER); - } - if (hMessageInput) { - SetWindowPos(hMessageInput, NULL, 320, height - 200, width - 600, 60, SWP_NOZORDER); - } - if (hSendButton) { - SetWindowPos(hSendButton, NULL, 320, height - 130, 100, 45, SWP_NOZORDER); - } - if (hShareFileButton) { - SetWindowPos(hShareFileButton, NULL, 440, height - 130, 120, 45, SWP_NOZORDER); - } - } + ResizeChatUI(hWnd); break; case WM_CTLCOLORSTATIC: @@ -879,6 +209,7 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) if (wmEvent == LBN_SELCHANGE) { int selectedIndex = (int)::SendMessage(hContactsList, LB_GETCURSEL, 0, 0); LoadContactChat(selectedIndex); + UpdateSharedFilesList(); } break; case IDC_SEND_BUTTON: @@ -910,41 +241,7 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) break; case WM_TIMER: - { - if (wParam == 1 && selectedContactIndex >= 0) { - KillTimer(hWnd, 1); - - // Auto-reply from the selected contact with more variety - std::vector autoReplies = { - L"Got it!", - L"Thanks for letting me know!", - L"Sounds good!", - L"I'll get back to you soon.", - L"Perfect!", - L"Absolutely!", - L"Let me think about it.", - L"Great idea!" - }; - - int replyIndex = rand() % autoReplies.size(); - AddMessageToChat(autoReplies[replyIndex], false); - } - else if (wParam == 2 && selectedContactIndex >= 0) { - KillTimer(hWnd, 2); - - // Auto-reply acknowledging the shared file with emojis - std::vector fileReplies = { - L"Thanks for sharing the file!", - L"Got the file, will check it out.", - L"File received, thanks! ?", - L"Perfect timing, I needed this file.", - L"Awesome, downloading now!" - }; - - int replyIndex = rand() % fileReplies.size(); - AddMessageToChat(fileReplies[replyIndex], false); - } - } + ProcessAutoReply(hWnd, (int)wParam); break; case WM_KEYDOWN: @@ -989,20 +286,3 @@ INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) } return (INT_PTR)FALSE; } - -void SendChatMessage() -{ - if (selectedContactIndex < 0) return; - - WCHAR buffer[1024]; - GetWindowText(hMessageInput, buffer, 1024); - - std::wstring message(buffer); - if (!message.empty()) { - AddMessageToChat(message, true); - SetWindowText(hMessageInput, L""); - - // Simulate auto-reply after a short delay - SetTimer(GetParent(hMessageInput), 1, 2000, NULL); - } -} diff --git a/Samples/SampleChatAppWithShare/SampleChatAppWithShare/SampleChatAppWithShare.exe.manifest b/Samples/SampleChatAppWithShare/SampleChatAppWithShare/SampleChatAppWithShare.exe.manifest new file mode 100644 index 0000000..92ae86a --- /dev/null +++ b/Samples/SampleChatAppWithShare/SampleChatAppWithShare/SampleChatAppWithShare.exe.manifest @@ -0,0 +1,9 @@ + + + + + diff --git a/Samples/SampleChatAppWithShare/SampleChatAppWithShare/SampleChatAppWithShare.vcxproj b/Samples/SampleChatAppWithShare/SampleChatAppWithShare/SampleChatAppWithShare.vcxproj index 9f6f5e2..675727d 100644 --- a/Samples/SampleChatAppWithShare/SampleChatAppWithShare/SampleChatAppWithShare.vcxproj +++ b/Samples/SampleChatAppWithShare/SampleChatAppWithShare/SampleChatAppWithShare.vcxproj @@ -176,13 +176,26 @@ + + + + + + + + + + + + + @@ -191,6 +204,9 @@ + + + diff --git a/Samples/SampleChatAppWithShare/SampleChatAppWithShare/SampleChatAppWithShare.vcxproj.filters b/Samples/SampleChatAppWithShare/SampleChatAppWithShare/SampleChatAppWithShare.vcxproj.filters index a39e463..a94fb95 100644 --- a/Samples/SampleChatAppWithShare/SampleChatAppWithShare/SampleChatAppWithShare.vcxproj.filters +++ b/Samples/SampleChatAppWithShare/SampleChatAppWithShare/SampleChatAppWithShare.vcxproj.filters @@ -27,11 +27,50 @@ Header Files + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + Source Files + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + @@ -46,4 +85,7 @@ Resource Files + + + \ No newline at end of file diff --git a/Samples/SampleChatAppWithShare/SampleChatAppWithShare/UIConstants.h b/Samples/SampleChatAppWithShare/SampleChatAppWithShare/UIConstants.h new file mode 100644 index 0000000..ff28af5 --- /dev/null +++ b/Samples/SampleChatAppWithShare/SampleChatAppWithShare/UIConstants.h @@ -0,0 +1,23 @@ +#pragma once + +#include + +// Include resource.h for resource constants +#include "resource.h" + +// Modern UI Color Scheme +#define COLOR_PRIMARY RGB(64, 128, 255) // Modern blue +#define COLOR_PRIMARY_DARK RGB(45, 100, 220) // Darker blue for hover +#define COLOR_APP_BACKGROUND RGB(248, 249, 250) // Light gray background +#define COLOR_SURFACE RGB(255, 255, 255) // White surface +#define COLOR_TEXT_PRIMARY RGB(33, 37, 41) // Dark text +#define COLOR_TEXT_SECONDARY RGB(108, 117, 125) // Gray text +#define COLOR_BORDER RGB(222, 226, 230) // Light border +#define COLOR_HOVER RGB(248, 249, 250) // Hover background +#define COLOR_CHAT_BUBBLE_OUT RGB(0, 123, 255) // Outgoing message +#define COLOR_CHAT_BUBBLE_IN RGB(233, 236, 239) // Incoming message + +// UI Constants +#define MAX_LOADSTRING 100 +#define CONTACT_ITEM_HEIGHT 72 +#define AVATAR_SIZE 40 \ No newline at end of file diff --git a/Samples/SampleChatAppWithShare/SampleChatAppWithShare/UIManager.cpp b/Samples/SampleChatAppWithShare/SampleChatAppWithShare/UIManager.cpp new file mode 100644 index 0000000..bdd4808 --- /dev/null +++ b/Samples/SampleChatAppWithShare/SampleChatAppWithShare/UIManager.cpp @@ -0,0 +1,142 @@ +#include "UIManager.h" +#include "UIConstants.h" +#include "ModernUI.h" +#include "ChatModels.h" +#include "FileManager.h" +#include "WindowProcs.h" +#include + +#pragma comment(lib, "comctl32.lib") + +// External declaration - hInst is defined in the main cpp file +extern HINSTANCE hInst; + +// UI Window handles definitions +HWND hContactsList = nullptr; +HWND hChatDisplay = nullptr; +HWND hMessageInput = nullptr; +HWND hSendButton = nullptr; +HWND hContactName = nullptr; +HWND hShareFileButton = nullptr; +HWND hSharedFilesList = nullptr; + +void CreateChatUI(HWND hWnd) +{ + RECT rect; + GetClientRect(hWnd, &rect); + + int width = rect.right - rect.left; + int height = rect.bottom - rect.top; + + // Set window background to modern color + SetClassLongPtr(hWnd, GCLP_HBRBACKGROUND, (LONG_PTR)hBrushBackground); + + // Create contacts list (left panel) with modern styling + hContactsList = CreateWindowEx( + WS_EX_CLIENTEDGE, + L"LISTBOX", NULL, + WS_CHILD | WS_VISIBLE | WS_VSCROLL | LBS_NOTIFY | LBS_OWNERDRAWFIXED, + 20, 20, 280, height - 40, + hWnd, (HMENU)IDC_CONTACTS_LIST, hInst, NULL); + + // Set custom item height for contact list + ::SendMessage(hContactsList, LB_SETITEMHEIGHT, 0, CONTACT_ITEM_HEIGHT); + + // Create contact name label with modern styling + hContactName = CreateWindow(L"STATIC", L"Select a contact to start chatting", + WS_CHILD | WS_VISIBLE | SS_LEFT, + 320, 20, 300, 30, + hWnd, (HMENU)IDC_CONTACT_NAME, hInst, NULL); + + // Create chat display area with modern styling + hChatDisplay = CreateWindowEx( + WS_EX_CLIENTEDGE, + L"EDIT", NULL, + WS_CHILD | WS_VISIBLE | WS_VSCROLL | ES_MULTILINE | ES_READONLY | ES_AUTOVSCROLL, + 320, 60, width - 600, height - 280, + hWnd, (HMENU)IDC_CHAT_DISPLAY, hInst, NULL); + + // Create shared files section header + CreateWindow(L"STATIC", L"Shared Files", + WS_CHILD | WS_VISIBLE | SS_LEFT, + width - 260, 20, 120, 30, + hWnd, NULL, hInst, NULL); + + // Create shared files list with modern styling + hSharedFilesList = CreateWindowEx( + WS_EX_CLIENTEDGE, + L"LISTBOX", NULL, + WS_CHILD | WS_VISIBLE | WS_VSCROLL | LBS_NOTIFY, + width - 260, 60, 240, height - 280, + hWnd, (HMENU)IDC_SHARED_FILES_LIST, hInst, NULL); + + // Create message input with placeholder styling + hMessageInput = CreateWindowEx( + WS_EX_CLIENTEDGE, + L"EDIT", NULL, + WS_CHILD | WS_VISIBLE | ES_MULTILINE | ES_AUTOVSCROLL, + 320, height - 200, width - 600, 60, + hWnd, (HMENU)IDC_MESSAGE_INPUT, hInst, NULL); + + // Create modern styled buttons + hSendButton = CreateWindow(L"BUTTON", L"Send", + WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | BS_OWNERDRAW, + 320, height - 130, 100, 45, + hWnd, (HMENU)IDC_SEND_BUTTON, hInst, NULL); + + hShareFileButton = CreateWindow(L"BUTTON", L"Share File", + WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | BS_OWNERDRAW, + 440, height - 130, 120, 45, + hWnd, (HMENU)IDC_SHARE_FILE_BUTTON, hInst, NULL); + + // Populate contacts list + for (const auto& contact : contacts) { + ::SendMessage(hContactsList, LB_ADDSTRING, 0, (LPARAM)contact.name.c_str()); + } + + // Apply modern fonts to controls + ::SendMessage(hContactName, WM_SETFONT, (WPARAM)hFontTitle, TRUE); + ::SendMessage(hChatDisplay, WM_SETFONT, (WPARAM)hFontRegular, TRUE); + ::SendMessage(hMessageInput, WM_SETFONT, (WPARAM)hFontRegular, TRUE); + ::SendMessage(hSharedFilesList, WM_SETFONT, (WPARAM)hFontRegular, TRUE); + + // Subclass buttons for custom drawing + SetupCustomWindowProcs(); + + // Set modern background colors + SetupWindowColors(); +} + +void ResizeChatUI(HWND hWnd) +{ + RECT rect; + GetClientRect(hWnd, &rect); + int width = rect.right - rect.left; + int height = rect.bottom - rect.top; + + if (hContactsList) { + SetWindowPos(hContactsList, NULL, 20, 20, 280, height - 40, SWP_NOZORDER); + } + if (hChatDisplay) { + SetWindowPos(hChatDisplay, NULL, 320, 60, width - 600, height - 280, SWP_NOZORDER); + } + if (hSharedFilesList) { + SetWindowPos(hSharedFilesList, NULL, width - 260, 60, 240, height - 280, SWP_NOZORDER); + } + if (hMessageInput) { + SetWindowPos(hMessageInput, NULL, 320, height - 200, width - 600, 60, SWP_NOZORDER); + } + if (hSendButton) { + SetWindowPos(hSendButton, NULL, 320, height - 130, 100, 45, SWP_NOZORDER); + } + if (hShareFileButton) { + SetWindowPos(hShareFileButton, NULL, 440, height - 130, 120, 45, SWP_NOZORDER); + } +} + +void SetupWindowColors() +{ + SetClassLongPtr(hChatDisplay, GCLP_HBRBACKGROUND, (LONG_PTR)hBrushSurface); + SetClassLongPtr(hMessageInput, GCLP_HBRBACKGROUND, (LONG_PTR)hBrushSurface); + SetClassLongPtr(hSharedFilesList, GCLP_HBRBACKGROUND, (LONG_PTR)hBrushSurface); +} \ No newline at end of file diff --git a/Samples/SampleChatAppWithShare/SampleChatAppWithShare/UIManager.h b/Samples/SampleChatAppWithShare/SampleChatAppWithShare/UIManager.h new file mode 100644 index 0000000..74c2a14 --- /dev/null +++ b/Samples/SampleChatAppWithShare/SampleChatAppWithShare/UIManager.h @@ -0,0 +1,17 @@ +#pragma once + +#include + +// UI Window handles +extern HWND hContactsList; +extern HWND hChatDisplay; +extern HWND hMessageInput; +extern HWND hSendButton; +extern HWND hContactName; +extern HWND hShareFileButton; +extern HWND hSharedFilesList; + +// UI management functions +void CreateChatUI(HWND hWnd); +void ResizeChatUI(HWND hWnd); +void SetupWindowColors(); \ No newline at end of file diff --git a/Samples/SampleChatAppWithShare/SampleChatAppWithShare/WindowProcs.cpp b/Samples/SampleChatAppWithShare/SampleChatAppWithShare/WindowProcs.cpp new file mode 100644 index 0000000..8a0658c --- /dev/null +++ b/Samples/SampleChatAppWithShare/SampleChatAppWithShare/WindowProcs.cpp @@ -0,0 +1,127 @@ +#include "WindowProcs.h" +#include "UIConstants.h" +#include "ModernUI.h" +#include "ChatModels.h" +#include "UIManager.h" + +// External declarations for UI window handles +extern HWND hSendButton; +extern HWND hShareFileButton; +extern HWND hContactsList; + +// Window procedure storage for subclassing +WNDPROC originalButtonProc = nullptr; +WNDPROC originalListBoxProc = nullptr; + +void SetupCustomWindowProcs() +{ + // Subclass buttons for custom drawing + originalButtonProc = (WNDPROC)SetWindowLongPtr(hSendButton, GWLP_WNDPROC, (LONG_PTR)ModernButtonProc); + SetWindowLongPtr(hShareFileButton, GWLP_WNDPROC, (LONG_PTR)ModernButtonProc); + + // Subclass contact list for custom drawing + originalListBoxProc = (WNDPROC)SetWindowLongPtr(hContactsList, GWLP_WNDPROC, (LONG_PTR)ModernListBoxProc); +} + +// Modern button subclass procedure +LRESULT CALLBACK ModernButtonProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + static bool isHovered = false; + static bool isPressed = false; + + switch (msg) + { + case WM_PAINT: + { + PAINTSTRUCT ps; + HDC hdc = BeginPaint(hWnd, &ps); + + RECT rect; + GetClientRect(hWnd, &rect); + + WCHAR text[256]; + GetWindowText(hWnd, text, 256); + + DrawModernButton(hdc, rect, std::wstring(text), isHovered, isPressed); + + EndPaint(hWnd, &ps); + return 0; + } + + case WM_MOUSEMOVE: + if (!isHovered) + { + isHovered = true; + InvalidateRect(hWnd, NULL, FALSE); + + TRACKMOUSEEVENT tme = {}; + tme.cbSize = sizeof(tme); + tme.dwFlags = TME_LEAVE; + tme.hwndTrack = hWnd; + TrackMouseEvent(&tme); + } + break; + + case WM_MOUSELEAVE: + isHovered = false; + InvalidateRect(hWnd, NULL, FALSE); + break; + + case WM_LBUTTONDOWN: + isPressed = true; + InvalidateRect(hWnd, NULL, FALSE); + SetCapture(hWnd); + break; + + case WM_LBUTTONUP: + if (isPressed) + { + isPressed = false; + InvalidateRect(hWnd, NULL, FALSE); + ReleaseCapture(); + + // Send click notification to parent + HWND hParent = GetParent(hWnd); + int controlId = GetDlgCtrlID(hWnd); + ::SendMessage(hParent, WM_COMMAND, MAKEWPARAM(controlId, BN_CLICKED), (LPARAM)hWnd); + } + break; + } + + return CallWindowProc(originalButtonProc, hWnd, msg, wParam, lParam); +} + +// Modern listbox subclass procedure +LRESULT CALLBACK ModernListBoxProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + switch (msg) + { + case WM_PAINT: + if (hWnd == hContactsList) + { + PAINTSTRUCT ps; + HDC hdc = BeginPaint(hWnd, &ps); + + RECT clientRect; + GetClientRect(hWnd, &clientRect); + + // Fill background + FillRect(hdc, &clientRect, hBrushSurface); + + int itemCount = contacts.size(); + int selectedIndex = ::SendMessage(hWnd, LB_GETCURSEL, 0, 0); + + for (int i = 0; i < itemCount; i++) + { + RECT itemRect = {0, i * CONTACT_ITEM_HEIGHT, clientRect.right, (i + 1) * CONTACT_ITEM_HEIGHT}; + DrawContactItem(hdc, itemRect, contacts[i], i == selectedIndex); + } + + EndPaint(hWnd, &ps); + return 0; + } + break; + } + + return CallWindowProc(originalListBoxProc, hWnd, msg, wParam, lParam); +} \ No newline at end of file diff --git a/Samples/SampleChatAppWithShare/SampleChatAppWithShare/WindowProcs.h b/Samples/SampleChatAppWithShare/SampleChatAppWithShare/WindowProcs.h new file mode 100644 index 0000000..97c8623 --- /dev/null +++ b/Samples/SampleChatAppWithShare/SampleChatAppWithShare/WindowProcs.h @@ -0,0 +1,14 @@ +#pragma once + +#include + +// Window procedures +LRESULT CALLBACK ModernButtonProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); +LRESULT CALLBACK ModernListBoxProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); + +// Original window procedures storage +extern WNDPROC originalButtonProc; +extern WNDPROC originalListBoxProc; + +// Setup functions +void SetupCustomWindowProcs(); \ No newline at end of file From b7014fc1a8cef39abe3fbe5922651f6613f6050e Mon Sep 17 00:00:00 2001 From: Ravish Roshan Date: Tue, 23 Sep 2025 16:39:27 +0530 Subject: [PATCH 04/20] Added C++17 support --- .../SampleChatAppWithShare/SampleChatAppWithShare.vcxproj | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Samples/SampleChatAppWithShare/SampleChatAppWithShare/SampleChatAppWithShare.vcxproj b/Samples/SampleChatAppWithShare/SampleChatAppWithShare/SampleChatAppWithShare.vcxproj index 675727d..9ed955b 100644 --- a/Samples/SampleChatAppWithShare/SampleChatAppWithShare/SampleChatAppWithShare.vcxproj +++ b/Samples/SampleChatAppWithShare/SampleChatAppWithShare/SampleChatAppWithShare.vcxproj @@ -103,6 +103,7 @@ true WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions) true + stdcpp17 Windows @@ -117,6 +118,7 @@ true WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions) true + stdcpp17 Windows @@ -129,6 +131,7 @@ true _DEBUG;_WINDOWS;%(PreprocessorDefinitions) true + stdcpp17 Windows @@ -143,6 +146,7 @@ true NDEBUG;_WINDOWS;%(PreprocessorDefinitions) true + stdcpp17 Windows @@ -155,6 +159,7 @@ true _DEBUG;_WINDOWS;%(PreprocessorDefinitions) true + stdcpp17 Windows @@ -169,6 +174,7 @@ true NDEBUG;_WINDOWS;%(PreprocessorDefinitions) true + stdcpp17 Windows From 258a75163087b95213bb6fc5b3dfeb364fc96dc7 Mon Sep 17 00:00:00 2001 From: Ravish Roshan Date: Tue, 23 Sep 2025 17:38:59 +0530 Subject: [PATCH 05/20] Add package identity management system --- .../PackageIdentity.cpp | 463 ++++++++++++++++++ .../SampleChatAppWithShare/PackageIdentity.h | 23 + .../SampleChatAppWithShare.cpp | 39 ++ .../SampleChatAppWithShare.vcxproj | 2 + .../SampleChatAppWithShare.vcxproj.filters | 6 + 5 files changed, 533 insertions(+) create mode 100644 Samples/SampleChatAppWithShare/SampleChatAppWithShare/PackageIdentity.cpp create mode 100644 Samples/SampleChatAppWithShare/SampleChatAppWithShare/PackageIdentity.h diff --git a/Samples/SampleChatAppWithShare/SampleChatAppWithShare/PackageIdentity.cpp b/Samples/SampleChatAppWithShare/SampleChatAppWithShare/PackageIdentity.cpp new file mode 100644 index 0000000..4f4ace9 --- /dev/null +++ b/Samples/SampleChatAppWithShare/SampleChatAppWithShare/PackageIdentity.cpp @@ -0,0 +1,463 @@ +#include "framework.h" +#include "PackageIdentity.h" +#include +#include +#include +#include +#include + +// Package Identity Variables +bool g_isSparsePackageSupported = false; +bool g_isRunningWithIdentity = false; +bool g_packageIdentityInitialized = false; + +// Initialize package identity management +bool InitializePackageIdentity() +{ + if (g_packageIdentityInitialized) + return true; + + OutputDebugStringW(L"ChatApp: Initializing package identity management...\n"); + + // Check OS support for sparse packages + g_isSparsePackageSupported = IsSparsePackageSupported(); + + // Check if already running with identity + g_isRunningWithIdentity = IsRunningWithIdentity(); + + g_packageIdentityInitialized = true; + + wchar_t statusLog[256]; + swprintf_s(statusLog, L"ChatApp: Package Identity Status - Sparse supported: %s, Has identity: %s\n", + g_isSparsePackageSupported ? L"Yes" : L"No", + g_isRunningWithIdentity ? L"Yes" : L"No"); + OutputDebugStringW(statusLog); + + return true; +} + +// Register package with external location (improved implementation) +HRESULT RegisterPackageWithExternalLocation(const std::wstring& externalLocation, const std::wstring& packagePath) +{ + try + { + OutputDebugStringW(L"ChatApp: Attempting to register package with external location...\n"); + + wchar_t logBuffer[512]; + swprintf_s(logBuffer, L"ChatApp: External location: %s\n", externalLocation.c_str()); + OutputDebugStringW(logBuffer); + swprintf_s(logBuffer, L"ChatApp: Package path: %s\n", packagePath.c_str()); + OutputDebugStringW(logBuffer); + + // Check if the package file exists + DWORD fileAttributes = GetFileAttributesW(packagePath.c_str()); + if (fileAttributes == INVALID_FILE_ATTRIBUTES) + { + OutputDebugStringW(L"ChatApp: Package file not found, trying PowerShell registration method.\n"); + return RegisterPackageWithExternalLocationPowerShell(externalLocation, packagePath); + } + + // Try PowerShell registration first as it's more reliable + HRESULT powershellResult = RegisterPackageWithExternalLocationPowerShell(externalLocation, packagePath); + if (SUCCEEDED(powershellResult)) + { + OutputDebugStringW(L"ChatApp: Package registration via PowerShell succeeded.\n"); + return powershellResult; + } + + // If PowerShell failed, log the error + wchar_t errorLog[256]; + swprintf_s(errorLog, L"ChatApp: PowerShell registration failed with HRESULT: 0x%08X\n", powershellResult); + OutputDebugStringW(errorLog); + + // For now, return the PowerShell result since we don't have other registration methods implemented + return powershellResult; + } + catch (...) + { + OutputDebugStringW(L"ChatApp: Exception occurred during package registration\n"); + return E_FAIL; + } +} + +// Alternative implementation using PowerShell for package registration +HRESULT RegisterPackageWithExternalLocationPowerShell(const std::wstring& externalLocation, const std::wstring& packagePath) +{ + try + { + OutputDebugStringW(L"ChatApp: Attempting PowerShell package registration...\n"); + + // Check if the package file exists + DWORD fileAttributes = GetFileAttributesW(packagePath.c_str()); + if (fileAttributes == INVALID_FILE_ATTRIBUTES) + { + wchar_t errorLog[512]; + swprintf_s(errorLog, L"ChatApp: Package file not found at: %s\n", packagePath.c_str()); + OutputDebugStringW(errorLog); + return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); + } + + // Build PowerShell command for MSIX package registration with external location + // Use Add-AppxPackage with -ExternalLocation parameter + std::wstring powershellCmd = L"powershell.exe -ExecutionPolicy Bypass -Command \""; + powershellCmd += L"try { "; + powershellCmd += L"Add-AppxPackage -Path '"; + powershellCmd += packagePath; + powershellCmd += L"' -ExternalLocation '"; + powershellCmd += externalLocation; + powershellCmd += L"' -ForceTargetApplicationShutdown; "; + powershellCmd += L"Write-Host 'Package registration successful'; "; + powershellCmd += L"exit 0; "; + powershellCmd += L"} catch { "; + powershellCmd += L"Write-Host ('Package registration failed: ' + $_.Exception.Message); "; + powershellCmd += L"exit 1; "; + powershellCmd += L"}\""; + + wchar_t logBuffer[1024]; + swprintf_s(logBuffer, L"ChatApp: PowerShell command: %s\n", powershellCmd.c_str()); + OutputDebugStringW(logBuffer); + + // Execute PowerShell command + STARTUPINFOW si = {}; + PROCESS_INFORMATION pi = {}; + si.cb = sizeof(si); + si.dwFlags = STARTF_USESHOWWINDOW; + si.wShowWindow = SW_HIDE; + + BOOL success = CreateProcessW( + nullptr, + const_cast(powershellCmd.c_str()), + nullptr, + nullptr, + FALSE, + CREATE_NO_WINDOW, + nullptr, + nullptr, + &si, + &pi + ); + + if (success) + { + OutputDebugStringW(L"ChatApp: PowerShell process started, waiting for completion...\n"); + + // Wait for PowerShell to complete (with timeout) + DWORD waitResult = WaitForSingleObject(pi.hProcess, 60000); // Increased timeout for package registration + + DWORD exitCode = 0; + if (waitResult == WAIT_OBJECT_0) + { + GetExitCodeProcess(pi.hProcess, &exitCode); + wchar_t exitLog[256]; + swprintf_s(exitLog, L"ChatApp: PowerShell process completed with exit code: %d\n", exitCode); + OutputDebugStringW(exitLog); + } + else if (waitResult == WAIT_TIMEOUT) + { + OutputDebugStringW(L"ChatApp: PowerShell process timed out. Package registration may still be in progress.\n"); + TerminateProcess(pi.hProcess, 1); + exitCode = 1; + } + else + { + DWORD waitError = GetLastError(); + wchar_t waitLog[256]; + swprintf_s(waitLog, L"ChatApp: Wait failed with error: %d\n", waitError); + OutputDebugStringW(waitLog); + exitCode = 1; + } + + CloseHandle(pi.hProcess); + CloseHandle(pi.hThread); + + return (exitCode == 0) ? S_OK : E_FAIL; + } + else + { + DWORD error = GetLastError(); + wchar_t errorLog[256]; + swprintf_s(errorLog, L"ChatApp: Failed to start PowerShell process. Error: %d\n", error); + OutputDebugStringW(errorLog); + return HRESULT_FROM_WIN32(error); + } + } + catch (...) + { + OutputDebugStringW(L"ChatApp: Exception occurred during PowerShell package registration\n"); + return E_FAIL; + } +} + +// Relaunch the current application +void RelaunchApplication() +{ + OutputDebugStringW(L"ChatApp: Attempting to relaunch application...\n"); + + wchar_t exePath[MAX_PATH] = {0}; + DWORD len = GetModuleFileNameW(nullptr, exePath, MAX_PATH); + if (len == 0 || len == MAX_PATH) + { + OutputDebugStringW(L"ChatApp: Failed to get executable path for relaunch.\n"); + return; + } + + // Log the executable path + wchar_t logBuffer[512]; + swprintf_s(logBuffer, L"ChatApp: Relaunching: %s\n", exePath); + OutputDebugStringW(logBuffer); + + // Add a small delay to allow package registration to complete + Sleep(2000); + + // Use ShellExecuteW to relaunch the current executable + HINSTANCE result = ShellExecuteW(nullptr, L"open", exePath, nullptr, nullptr, SW_SHOWNORMAL); + + // Fix: Use proper casting for x64 compatibility + INT_PTR resultValue = reinterpret_cast(result); + if (resultValue <= 32) + { + // Log the error + wchar_t errorLog[256]; + swprintf_s(errorLog, L"ChatApp: Failed to relaunch application. ShellExecute error code: %lld\n", + static_cast(resultValue)); + OutputDebugStringW(errorLog); + + // Try alternative relaunch method using CreateProcess + OutputDebugStringW(L"ChatApp: Trying alternative relaunch method...\n"); + + STARTUPINFOW si = {}; + PROCESS_INFORMATION pi = {}; + si.cb = sizeof(si); + si.dwFlags = STARTF_USESHOWWINDOW; + si.wShowWindow = SW_SHOWNORMAL; + + BOOL createResult = CreateProcessW( + exePath, + nullptr, + nullptr, + nullptr, + FALSE, + 0, + nullptr, + nullptr, + &si, + &pi + ); + + if (createResult) + { + OutputDebugStringW(L"ChatApp: Alternative relaunch method succeeded.\n"); + CloseHandle(pi.hProcess); + CloseHandle(pi.hThread); + } + else + { + DWORD createError = GetLastError(); + wchar_t createErrorLog[256]; + swprintf_s(createErrorLog, L"ChatApp: Alternative relaunch also failed. Error: %d\n", createError); + OutputDebugStringW(createErrorLog); + } + } + else + { + OutputDebugStringW(L"ChatApp: Application relaunch initiated successfully.\n"); + } +} + +// Checks if the OS version is Windows 10 2004 (build 19041) or later +bool IsSparsePackageSupported() +{ + // Windows 10 2004 is version 10.0.19041 + OSVERSIONINFOEXW osvi = {}; + osvi.dwOSVersionInfoSize = sizeof(osvi); + + // Get the actual version using RtlGetVersion (undocumented but reliable) + typedef LONG (WINAPI* RtlGetVersionPtr)(PRTL_OSVERSIONINFOEXW); + HMODULE hMod = ::GetModuleHandleW(L"ntdll.dll"); + if (hMod) { + RtlGetVersionPtr fxPtr = (RtlGetVersionPtr)::GetProcAddress(hMod, "RtlGetVersion"); + if (fxPtr != nullptr) { + fxPtr((PRTL_OSVERSIONINFOEXW)&osvi); + + // Log version information for debugging + wchar_t log[256]; + swprintf_s(log, L"ChatApp: Current OS Version: %u.%u.%u\n", + osvi.dwMajorVersion, osvi.dwMinorVersion, osvi.dwBuildNumber); + OutputDebugStringW(log); + } + } + + // Compare with required version (Windows 10 2004 build 19041) + if (osvi.dwMajorVersion > 10 || + (osvi.dwMajorVersion == 10 && osvi.dwMinorVersion > 0) || + (osvi.dwMajorVersion == 10 && osvi.dwMinorVersion == 0 && osvi.dwBuildNumber >= 19041)) + { + OutputDebugStringW(L"ChatApp: Sparse package is supported on this OS.\n"); + return true; + } + + OutputDebugStringW(L"ChatApp: Sparse package is NOT supported on this OS.\n"); + return false; +} + +// Returns true if the app is running with package identity +bool IsRunningWithIdentity() +{ + UINT32 length = 0; + LONG rc = GetCurrentPackageFullName(&length, nullptr); + + if (rc == ERROR_INSUFFICIENT_BUFFER) + { + std::vector packageFullName(length); + rc = GetCurrentPackageFullName(&length, packageFullName.data()); + if (rc == ERROR_SUCCESS) + { + OutputDebugStringW(L"ChatApp: Running with package identity.\n"); + return true; + } + } + + OutputDebugStringW(L"ChatApp: Not running with package identity.\n"); + return false; +} + +// Helper to get the directory of the current executable +std::wstring GetExecutableDirectory() +{ + wchar_t exePath[MAX_PATH] = {0}; + DWORD len = GetModuleFileNameW(nullptr, exePath, MAX_PATH); + if (len == 0 || len == MAX_PATH) + { + OutputDebugStringW(L"ChatApp: Failed to get executable path.\n"); + return L""; + } + + std::wstring path(exePath); + size_t pos = path.find_last_of(L"\\/"); + if (pos != std::wstring::npos) + path = path.substr(0, pos); + + return path; +} + +// Initialize package identity-specific flows and features +void InitializePackageIdentityFlow() +{ + OutputDebugStringW(L"ChatApp: Initializing package identity-specific flows...\n"); + + if (!g_isRunningWithIdentity) { + OutputDebugStringW(L"ChatApp: Not running with package identity - skipping identity-specific flows.\n"); + return; + } + + // TODO: Add package identity-specific initialization here: + + // 1. Single Instance Management + // - Check for existing app instances + // - Register current instance + // - Handle instance redirection + + // 2. Share Target Registration + // - Check for share target activation + // - Initialize share target handlers + // - Set up cross-process communication + + // 3. Enhanced Security Features + // - Initialize secure file handling + // - Set up identity-based permissions + // - Enable enhanced data protection + + // 4. App Model Integration + // - Register activation handlers + // - Set up background task support + // - Initialize notification system + + OutputDebugStringW(L"ChatApp: Package identity flows initialized (placeholder - features to be implemented).\n"); +} + +// Helper to validate MSIX package existence and basic properties +bool ValidateMsixPackage(const std::wstring& packagePath) +{ + OutputDebugStringW(L"ChatApp: Validating MSIX package...\n"); + + // Check if file exists + DWORD fileAttributes = GetFileAttributesW(packagePath.c_str()); + if (fileAttributes == INVALID_FILE_ATTRIBUTES) + { + wchar_t errorLog[512]; + swprintf_s(errorLog, L"ChatApp: MSIX package not found at: %s\n", packagePath.c_str()); + OutputDebugStringW(errorLog); + return false; + } + + // Check if it's a file (not a directory) + if (fileAttributes & FILE_ATTRIBUTE_DIRECTORY) + { + OutputDebugStringW(L"ChatApp: Package path points to a directory, not a file.\n"); + return false; + } + + // Check file extension + size_t dotPos = packagePath.find_last_of(L'.'); + if (dotPos == std::wstring::npos) + { + OutputDebugStringW(L"ChatApp: Package file has no extension.\n"); + return false; + } + + std::wstring extension = packagePath.substr(dotPos); + std::transform(extension.begin(), extension.end(), extension.begin(), ::towlower); + + if (extension != L".msix" && extension != L".appx") + { + wchar_t extLog[256]; + swprintf_s(extLog, L"ChatApp: Package has unexpected extension: %s\n", extension.c_str()); + OutputDebugStringW(extLog); + return false; + } + + // Get file size + HANDLE hFile = CreateFileW(packagePath.c_str(), GENERIC_READ, FILE_SHARE_READ, + nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr); + if (hFile != INVALID_HANDLE_VALUE) + { + LARGE_INTEGER fileSize; + if (GetFileSizeEx(hFile, &fileSize)) + { + wchar_t sizeLog[256]; + swprintf_s(sizeLog, L"ChatApp: Package size: %lld bytes\n", fileSize.QuadPart); + OutputDebugStringW(sizeLog); + } + CloseHandle(hFile); + } + + OutputDebugStringW(L"ChatApp: MSIX package validation passed.\n"); + return true; +} + +// Get current package identity status as a formatted string +std::wstring GetPackageIdentityStatus() +{ + std::wstring status = L"ChatApp Package Identity Status:\n"; + status += L"- Sparse package supported: " + std::wstring(g_isSparsePackageSupported ? L"Yes" : L"No") + L"\n"; + status += L"- Running with identity: " + std::wstring(g_isRunningWithIdentity ? L"Yes" : L"No") + L"\n"; + status += L"- Initialized: " + std::wstring(g_packageIdentityInitialized ? L"Yes" : L"No") + L"\n"; + + if (g_isRunningWithIdentity) + { + // Try to get package full name + UINT32 length = 0; + LONG rc = GetCurrentPackageFullName(&length, nullptr); + if (rc == ERROR_INSUFFICIENT_BUFFER && length > 0) + { + std::vector packageFullName(length); + rc = GetCurrentPackageFullName(&length, packageFullName.data()); + if (rc == ERROR_SUCCESS) + { + status += L"- Package full name: " + std::wstring(packageFullName.data()) + L"\n"; + } + } + } + + return status; +} \ No newline at end of file diff --git a/Samples/SampleChatAppWithShare/SampleChatAppWithShare/PackageIdentity.h b/Samples/SampleChatAppWithShare/SampleChatAppWithShare/PackageIdentity.h new file mode 100644 index 0000000..e97fa85 --- /dev/null +++ b/Samples/SampleChatAppWithShare/SampleChatAppWithShare/PackageIdentity.h @@ -0,0 +1,23 @@ +#pragma once + +#include +#include + +// Package Identity Variables +extern bool g_isSparsePackageSupported; +extern bool g_isRunningWithIdentity; +extern bool g_packageIdentityInitialized; + +// Package Identity Management functions +bool IsSparsePackageSupported(); +bool IsRunningWithIdentity(); +bool InitializePackageIdentity(); +void InitializePackageIdentityFlow(); +std::wstring GetExecutableDirectory(); +HRESULT RegisterPackageWithExternalLocation(const std::wstring& externalLocation, const std::wstring& packagePath); +HRESULT RegisterPackageWithExternalLocationPowerShell(const std::wstring& externalLocation, const std::wstring& packagePath); +void RelaunchApplication(); + +// Helper functions +bool ValidateMsixPackage(const std::wstring& packagePath); +std::wstring GetPackageIdentityStatus(); \ No newline at end of file diff --git a/Samples/SampleChatAppWithShare/SampleChatAppWithShare/SampleChatAppWithShare.cpp b/Samples/SampleChatAppWithShare/SampleChatAppWithShare/SampleChatAppWithShare.cpp index c1e4396..fe8a643 100644 --- a/Samples/SampleChatAppWithShare/SampleChatAppWithShare/SampleChatAppWithShare.cpp +++ b/Samples/SampleChatAppWithShare/SampleChatAppWithShare/SampleChatAppWithShare.cpp @@ -9,6 +9,7 @@ #include "ChatManager.h" #include "FileManager.h" #include "UIManager.h" +#include "PackageIdentity.h" #include "WindowProcs.h" #include #include @@ -38,6 +39,44 @@ int APIENTRY wWinMain(_In_ HINSTANCE hInstance, UNREFERENCED_PARAMETER(hPrevInstance); UNREFERENCED_PARAMETER(lpCmdLine); + // Init package identity checks + InitializePackageIdentity(); + + if (g_isSparsePackageSupported && !g_isRunningWithIdentity) + { + std::wstring executableDir = GetExecutableDirectory(); + std::wstring packagePath = executableDir + L"\\Weixin_1.0.1.0_x86__v4k3sbdawh17a.msix"; + + // Validate the MSIX package before attempting registration + if (ValidateMsixPackage(packagePath)) + { + HRESULT result = RegisterPackageWithExternalLocation(executableDir, packagePath); + if (SUCCEEDED(result)) + { + OutputDebugStringW(L"ChatApp: Package registration succeeded. Relaunching...\n"); + RelaunchApplication(); + return 0; // Exit after relaunch + } + else + { + // Log the error but continue without package identity + wchar_t errorLog[256]; + swprintf_s(errorLog, L"ChatApp: Failed to register package. HRESULT: 0x%08X. Continuing without package identity.\n", result); + OutputDebugStringW(errorLog); + } + } + else + { + OutputDebugStringW(L"ChatApp: MSIX package validation failed. Continuing without package identity.\n"); + } + } + else + { + // Log the current status + std::wstring status = GetPackageIdentityStatus(); + OutputDebugStringW(status.c_str()); + } + // Initialize COM for shell operations CoInitialize(NULL); diff --git a/Samples/SampleChatAppWithShare/SampleChatAppWithShare/SampleChatAppWithShare.vcxproj b/Samples/SampleChatAppWithShare/SampleChatAppWithShare/SampleChatAppWithShare.vcxproj index 9ed955b..310243e 100644 --- a/Samples/SampleChatAppWithShare/SampleChatAppWithShare/SampleChatAppWithShare.vcxproj +++ b/Samples/SampleChatAppWithShare/SampleChatAppWithShare/SampleChatAppWithShare.vcxproj @@ -187,6 +187,7 @@ + @@ -199,6 +200,7 @@ + diff --git a/Samples/SampleChatAppWithShare/SampleChatAppWithShare/SampleChatAppWithShare.vcxproj.filters b/Samples/SampleChatAppWithShare/SampleChatAppWithShare/SampleChatAppWithShare.vcxproj.filters index a94fb95..1bc4c91 100644 --- a/Samples/SampleChatAppWithShare/SampleChatAppWithShare/SampleChatAppWithShare.vcxproj.filters +++ b/Samples/SampleChatAppWithShare/SampleChatAppWithShare/SampleChatAppWithShare.vcxproj.filters @@ -48,6 +48,9 @@ Header Files + + Header Files + @@ -71,6 +74,9 @@ Source Files + + Source Files + From 82552d6fc4a917d1a0a501e4444071dc846a163d Mon Sep 17 00:00:00 2001 From: Ravish Roshan Date: Wed, 24 Sep 2025 04:10:01 +0530 Subject: [PATCH 06/20] Added support for share target --- .../SampleChatAppWithShare.cpp | 90 +++++- .../SampleChatAppWithShare.vcxproj | 2 + .../ShareTargetManager.cpp | 259 ++++++++++++++++++ .../ShareTargetManager.h | 33 +++ .../SampleChatAppWithShare/framework.h | 14 + 5 files changed, 397 insertions(+), 1 deletion(-) create mode 100644 Samples/SampleChatAppWithShare/SampleChatAppWithShare/ShareTargetManager.cpp create mode 100644 Samples/SampleChatAppWithShare/SampleChatAppWithShare/ShareTargetManager.h diff --git a/Samples/SampleChatAppWithShare/SampleChatAppWithShare/SampleChatAppWithShare.cpp b/Samples/SampleChatAppWithShare/SampleChatAppWithShare/SampleChatAppWithShare.cpp index fe8a643..23ddb2d 100644 --- a/Samples/SampleChatAppWithShare/SampleChatAppWithShare/SampleChatAppWithShare.cpp +++ b/Samples/SampleChatAppWithShare/SampleChatAppWithShare/SampleChatAppWithShare.cpp @@ -10,15 +10,28 @@ #include "FileManager.h" #include "UIManager.h" #include "PackageIdentity.h" +#include "ShareTargetManager.h" #include "WindowProcs.h" #include #include #include +#include +#include #pragma comment(lib, "comctl32.lib") #pragma comment(lib, "shell32.lib") #pragma comment(lib, "uxtheme.lib") #pragma comment(lib, "ole32.lib") +// Add WinRT libraries to fix linking error +#pragma comment(lib, "windowsapp.lib") +#pragma comment(lib, "runtimeobject.lib") + +using namespace winrt; +using namespace Windows::Foundation; +using namespace Windows::ApplicationModel; +using namespace Windows::ApplicationModel::Activation; +using namespace Windows::ApplicationModel::DataTransfer; +using namespace Windows::ApplicationModel::DataTransfer::ShareTarget; // Global Variables: HINSTANCE hInst; // current instance @@ -42,6 +55,26 @@ int APIENTRY wWinMain(_In_ HINSTANCE hInstance, // Init package identity checks InitializePackageIdentity(); + // Initialize WinRT with proper error handling + try + { + winrt::init_apartment(); + OutputDebugStringW(L"ChatApp: WinRT initialized successfully.\n"); + } + catch (hresult_error const& ex) + { + std::wstring errorMsg = L"ChatApp: WinRT initialization failed: " + std::wstring(ex.message().c_str()) + + L" (HRESULT: 0x" + std::to_wstring(static_cast(ex.code())) + L")\n"; + OutputDebugStringW(errorMsg.c_str()); + } + catch (...) + { + OutputDebugStringW(L"ChatApp: WinRT initialization failed with unknown error.\n"); + } + + // Initialize Share Target Manager + ShareTargetManager::Initialize(); + if (g_isSparsePackageSupported && !g_isRunningWithIdentity) { std::wstring executableDir = GetExecutableDirectory(); @@ -70,6 +103,11 @@ int APIENTRY wWinMain(_In_ HINSTANCE hInstance, OutputDebugStringW(L"ChatApp: MSIX package validation failed. Continuing without package identity.\n"); } } + else if (g_isRunningWithIdentity) + { + // Process share target activation using the ShareTargetManager + ShareTargetManager::ProcessActivationArgs(); + } else { // Log the current status @@ -273,6 +311,13 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) InvalidateRect(hSendButton, NULL, FALSE); } break; + case 2000: // Test WinRT Share Target Status + { + // Use ShareTargetManager to get status + std::wstring statusInfo = ShareTargetManager::GetShareTargetStatus(); + MessageBoxW(hWnd, statusInfo.c_str(), L"WinRT Share Target Status", MB_OK | MB_ICONINFORMATION); + } + break; default: return DefWindowProc(hWnd, message, wParam, lParam); } @@ -313,7 +358,50 @@ INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) switch (message) { case WM_INITDIALOG: - return (INT_PTR)TRUE; + { + // Add WinRT and package identity information to the about dialog + std::wstring aboutText = L"Chat Application with WinRT Share Target Support\n\n"; + + // Test WinRT status + try + { + winrt::check_hresult(S_OK); + aboutText += L"?? WinRT Status: ? Initialized and Working\n"; + + if (g_isRunningWithIdentity) + { + if (ShareTargetManager::IsShareTargetActivation()) + { + aboutText += L"?? Share Target: ? Currently Activated via Windows Share Sheet\n"; + } + else + { + aboutText += L"?? Share Target: ? Ready for Activation\n"; + } + } + else + { + aboutText += L"?? Share Target: ? Package Identity Required\n"; + } + } + catch (...) + { + aboutText += L"?? WinRT Status: ? Error or Not Available\n"; + } + + aboutText += L"?? Package Identity: " + std::wstring(g_isRunningWithIdentity ? L"? Available" : L"? Not Available") + L"\n"; + aboutText += L"?? Sparse Package Support: " + std::wstring(g_isSparsePackageSupported ? L"? Supported" : L"? Not Supported") + L"\n\n"; + aboutText += L"Current Features:\n"; + aboutText += L"? WinRT Runtime Integration\n"; + aboutText += L"? Windows Share Target Support\n"; + aboutText += L"? Package Identity Management\n"; + aboutText += L"? MSIX Package Registration\n"; + aboutText += L"? Modern Chat UI\n\n"; + aboutText += GetPackageIdentityStatus(); + + SetDlgItemTextW(hDlg, IDC_STATIC, aboutText.c_str()); + return (INT_PTR)TRUE; + } case WM_COMMAND: if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL) diff --git a/Samples/SampleChatAppWithShare/SampleChatAppWithShare/SampleChatAppWithShare.vcxproj b/Samples/SampleChatAppWithShare/SampleChatAppWithShare/SampleChatAppWithShare.vcxproj index 310243e..f98130f 100644 --- a/Samples/SampleChatAppWithShare/SampleChatAppWithShare/SampleChatAppWithShare.vcxproj +++ b/Samples/SampleChatAppWithShare/SampleChatAppWithShare/SampleChatAppWithShare.vcxproj @@ -190,6 +190,7 @@ + @@ -202,6 +203,7 @@ + diff --git a/Samples/SampleChatAppWithShare/SampleChatAppWithShare/ShareTargetManager.cpp b/Samples/SampleChatAppWithShare/SampleChatAppWithShare/ShareTargetManager.cpp new file mode 100644 index 0000000..178160d --- /dev/null +++ b/Samples/SampleChatAppWithShare/SampleChatAppWithShare/ShareTargetManager.cpp @@ -0,0 +1,259 @@ +#include "ShareTargetManager.h" +#include "PackageIdentity.h" +#include "framework.h" +#include + +// Static member initialization +bool ShareTargetManager::s_initialized = false; +bool ShareTargetManager::s_shareTargetSupported = false; + +void ShareTargetManager::Initialize() +{ + if (s_initialized) + return; + + LogShareInfo(L"Initializing Share Target Manager..."); + + try + { + // Check if WinRT and package identity are available + if (g_isRunningWithIdentity) + { + s_shareTargetSupported = true; + LogShareInfo(L"Share Target support available with package identity."); + } + else + { + s_shareTargetSupported = false; + LogShareInfo(L"Share Target requires package identity (not available)."); + } + } + catch (winrt::hresult_error const& ex) + { + std::wstring error = L"Share Target initialization error: " + std::wstring(ex.message().c_str()); + LogShareError(error); + s_shareTargetSupported = false; + } + catch (...) + { + LogShareError(L"Unknown error during Share Target initialization."); + s_shareTargetSupported = false; + } + + s_initialized = true; + LogShareInfo(L"Share Target Manager initialized successfully."); +} + +bool ShareTargetManager::IsShareTargetAvailable() +{ + if (!s_initialized) + Initialize(); + + return s_shareTargetSupported && g_isRunningWithIdentity; +} + +bool ShareTargetManager::IsShareTargetActivation() +{ + if (!IsShareTargetAvailable()) + return false; + + try + { + auto activationArgs = winrt::Windows::ApplicationModel::AppInstance::GetActivatedEventArgs(); + return activationArgs && activationArgs.Kind() == winrt::Windows::ApplicationModel::Activation::ActivationKind::ShareTarget; + } + catch (...) + { + LogShareError(L"Error checking share target activation."); + return false; + } +} + +bool ShareTargetManager::ProcessActivationArgs() +{ + if (!IsShareTargetAvailable()) + return false; + + try + { + auto activationArgs = winrt::Windows::ApplicationModel::AppInstance::GetActivatedEventArgs(); + if (activationArgs && activationArgs.Kind() == winrt::Windows::ApplicationModel::Activation::ActivationKind::ShareTarget) + { + LogShareInfo(L"? Share Target activation detected!"); + + // Process the share target directly here (same as original code) + auto shareArgs = activationArgs.as(); + auto shareOperation = shareArgs.ShareOperation(); + auto data = shareOperation.Data(); + + std::wstring shareInfo = L"Share Target Activated!\n\n"; + + // Check for different data formats + if (data.Contains(winrt::Windows::ApplicationModel::DataTransfer::StandardDataFormats::Text())) + { + try + { + auto textAsync = data.GetTextAsync(); + auto text = textAsync.get(); + shareInfo += L"?? Text: " + std::wstring(text.c_str()) + L"\n\n"; + LogShareInfo(L"Received shared text."); + } + catch (...) + { + shareInfo += L"?? Text: [Error retrieving text]\n\n"; + LogShareError(L"Error retrieving shared text."); + } + } + + if (data.Contains(winrt::Windows::ApplicationModel::DataTransfer::StandardDataFormats::WebLink())) + { + try + { + auto webLinkAsync = data.GetWebLinkAsync(); + auto webLink = webLinkAsync.get(); + shareInfo += L"?? Web Link: " + std::wstring(webLink.ToString().c_str()) + L"\n\n"; + LogShareInfo(L"Received shared web link."); + } + catch (...) + { + shareInfo += L"?? Web Link: [Error retrieving web link]\n\n"; + LogShareError(L"Error retrieving shared web link."); + } + } + + if (data.Contains(winrt::Windows::ApplicationModel::DataTransfer::StandardDataFormats::Bitmap())) + { + try + { + auto bitmapAsync = data.GetBitmapAsync(); + auto bitmapRef = bitmapAsync.get(); + shareInfo += L"??? Bitmap: Received image content\n\n"; + LogShareInfo(L"Received shared bitmap."); + } + catch (...) + { + shareInfo += L"??? Bitmap: [Error retrieving bitmap]\n\n"; + LogShareError(L"Error retrieving shared bitmap."); + } + } + + if (data.Contains(winrt::Windows::ApplicationModel::DataTransfer::StandardDataFormats::StorageItems())) + { + try + { + auto storageItemsAsync = data.GetStorageItemsAsync(); + auto storageItems = storageItemsAsync.get(); + shareInfo += L"?? Files: " + std::to_wstring(storageItems.Size()) + L" file(s) shared\n"; + + for (uint32_t i = 0; i < storageItems.Size(); i++) + { + auto item = storageItems.GetAt(i); + shareInfo += L" - " + std::wstring(item.Name().c_str()) + L"\n"; + } + shareInfo += L"\n"; + LogShareInfo(L"Received shared files."); + } + catch (...) + { + shareInfo += L"?? Files: [Error retrieving files]\n\n"; + LogShareError(L"Error retrieving shared files."); + } + } + + // Show the share information + MessageBoxW(nullptr, shareInfo.c_str(), L"Windows Share Target", MB_OK | MB_ICONINFORMATION); + + // Report completion + shareOperation.ReportCompleted(); + + LogShareInfo(L"Share target processing completed successfully."); + return true; + } + else + { + LogShareInfo(L"Running with package identity but not as share target."); + return false; + } + } + catch (winrt::hresult_error const& ex) + { + std::wstring error = L"Error checking activation args: " + std::wstring(ex.message().c_str()) + + L" (HRESULT: 0x" + std::to_wstring(static_cast(ex.code())) + L")"; + LogShareError(error); + MessageBoxW(nullptr, L"Error processing shared content", L"Share Target Error", MB_OK | MB_ICONERROR); + return false; + } + catch (...) + { + LogShareError(L"Unknown error checking activation arguments."); + MessageBoxW(nullptr, L"Unknown error processing shared content", L"Share Target Error", MB_OK | MB_ICONERROR); + return false; + } +} + +std::wstring ShareTargetManager::GetShareTargetStatus() +{ + if (!s_initialized) + Initialize(); + + std::wstring status = L"WinRT Share Target Integration Status:\n\n"; + + try + { + winrt::check_hresult(S_OK); + status += L"?? WinRT Runtime: ? Available and Working\n"; + status += L"?? Package Identity: " + std::wstring(g_isRunningWithIdentity ? L"? Available" : L"? Not Available") + L"\n"; + status += L"?? Sparse Package Support: " + std::wstring(g_isSparsePackageSupported ? L"? Supported" : L"? Not Supported") + L"\n\n"; + + // Test activation + if (g_isRunningWithIdentity) + { + try + { + if (IsShareTargetActivation()) + { + status += L"?? Share Target: ? Currently Activated\n"; + } + else + { + status += L"?? Share Target: ?? Not Currently Activated\n"; + } + } + catch (...) + { + status += L"?? Share Target: ? Error Checking Activation\n"; + } + } + else + { + status += L"?? Share Target: ? Package Identity Required\n"; + } + + status += GetPackageIdentityStatus(); + status += L"\n\n?? WinRT Share Target integration is complete and ready!"; + } + catch (winrt::hresult_error const& ex) + { + status += L"?? WinRT Runtime: ? Error\n"; + status += L"Error: " + std::wstring(ex.message().c_str()) + L"\n"; + status += L"HRESULT: 0x" + std::to_wstring(static_cast(ex.code())) + L"\n"; + } + catch (...) + { + status += L"?? WinRT Runtime: ? Unknown Error\n"; + } + + return status; +} + +void ShareTargetManager::LogShareInfo(const std::wstring& message) +{ + std::wstring logMessage = L"ChatApp: " + message + L"\n"; + OutputDebugStringW(logMessage.c_str()); +} + +void ShareTargetManager::LogShareError(const std::wstring& error) +{ + std::wstring logMessage = L"ChatApp: ShareTarget ERROR - " + error + L"\n"; + OutputDebugStringW(logMessage.c_str()); +} \ No newline at end of file diff --git a/Samples/SampleChatAppWithShare/SampleChatAppWithShare/ShareTargetManager.h b/Samples/SampleChatAppWithShare/SampleChatAppWithShare/ShareTargetManager.h new file mode 100644 index 0000000..2281410 --- /dev/null +++ b/Samples/SampleChatAppWithShare/SampleChatAppWithShare/ShareTargetManager.h @@ -0,0 +1,33 @@ +#pragma once + +#include +#include + +// Simple Share Target Manager with minimal dependencies +class ShareTargetManager +{ +public: + // Initialize the share target manager + static void Initialize(); + + // Check if the current activation is a share target + static bool IsShareTargetActivation(); + + // Check activation arguments and process if it's a share target + static bool ProcessActivationArgs(); + + // Get share target status information + static std::wstring GetShareTargetStatus(); + + // Check if share target is available (requires package identity) + static bool IsShareTargetAvailable(); + +private: + // Logging helpers + static void LogShareInfo(const std::wstring& message); + static void LogShareError(const std::wstring& error); + +private: + static bool s_initialized; + static bool s_shareTargetSupported; +}; \ No newline at end of file diff --git a/Samples/SampleChatAppWithShare/SampleChatAppWithShare/framework.h b/Samples/SampleChatAppWithShare/SampleChatAppWithShare/framework.h index 33a6be6..414c103 100644 --- a/Samples/SampleChatAppWithShare/SampleChatAppWithShare/framework.h +++ b/Samples/SampleChatAppWithShare/SampleChatAppWithShare/framework.h @@ -6,8 +6,22 @@ #include "targetver.h" #define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers + // Windows Header Files #include + +// WinRT Headers for Share Target functionality +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + // C RunTime Header Files #include #include From 01ab09c53bcffa6bdbfeaadf4fb358855af7629d Mon Sep 17 00:00:00 2001 From: Ravish Roshan Date: Wed, 24 Sep 2025 05:13:17 +0530 Subject: [PATCH 07/20] Add Contact Selection Dialog for file sharing --- .../ContactSelectionDialog.cpp | 275 ++++++++++++++++++ .../ContactSelectionDialog.h | 45 +++ .../SampleChatAppWithShare/FileManager.cpp | 160 +++++----- .../SampleChatAppWithShare/Resource.h | 14 +- .../SampleChatAppWithShare.rc | Bin 7124 -> 9184 bytes .../SampleChatAppWithShare.vcxproj | 8 + .../ShareTargetManager.cpp | 156 +++++++++- .../SampleChatAppWithShare/WindowProcs.cpp | 24 +- 8 files changed, 574 insertions(+), 108 deletions(-) create mode 100644 Samples/SampleChatAppWithShare/SampleChatAppWithShare/ContactSelectionDialog.cpp create mode 100644 Samples/SampleChatAppWithShare/SampleChatAppWithShare/ContactSelectionDialog.h diff --git a/Samples/SampleChatAppWithShare/SampleChatAppWithShare/ContactSelectionDialog.cpp b/Samples/SampleChatAppWithShare/SampleChatAppWithShare/ContactSelectionDialog.cpp new file mode 100644 index 0000000..ba1aeca --- /dev/null +++ b/Samples/SampleChatAppWithShare/SampleChatAppWithShare/ContactSelectionDialog.cpp @@ -0,0 +1,275 @@ +#include "ContactSelectionDialog.h" +#include "Resource.h" +#include "UIConstants.h" +#include + +// Static member initialization +ContactSelectionDialog::SelectionResult ContactSelectionDialog::s_dialogResult; +std::wstring ContactSelectionDialog::s_currentFilePath; +std::wstring ContactSelectionDialog::s_currentFileName; + +ContactSelectionDialog::SelectionResult ContactSelectionDialog::ShowContactSelectionDialog(HWND hParent, const std::wstring& filePath, const std::wstring& fileName) +{ + // Store the file information for the dialog + s_currentFilePath = filePath; + s_currentFileName = fileName; + + // Reset the result + s_dialogResult = SelectionResult(); + + // Show the modal dialog + INT_PTR result = DialogBox(GetModuleHandle(NULL), MAKEINTRESOURCE(IDD_CONTACT_SELECTION), hParent, ContactSelectionDlgProc); + + if (result == IDOK) + { + s_dialogResult.wasSelected = true; + s_dialogResult.filePath = s_currentFilePath; + s_dialogResult.fileName = s_currentFileName; + } + else + { + s_dialogResult.wasSelected = false; + } + + return s_dialogResult; +} + +INT_PTR CALLBACK ContactSelectionDialog::ContactSelectionDlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) +{ + switch (message) + { + case WM_INITDIALOG: + { + // Set the dialog title with file name + std::wstring title = L"Share \"" + s_currentFileName + L"\" - Select Contact"; + SetWindowText(hDlg, title.c_str()); + + // Set up the dialog layout and styling + SetupDialogSizing(hDlg); + + // Initialize and populate the contact list + HWND hListBox = GetDlgItem(hDlg, IDC_CONTACT_SELECTION_LIST); + if (hListBox) + { + InitializeContactList(hListBox); + PopulateContactList(hListBox); + } + + // Set up the share message edit control + HWND hMessageEdit = GetDlgItem(hDlg, IDC_SHARE_MESSAGE_EDIT); + if (hMessageEdit) + { + std::wstring defaultMessage = L"I'm sharing \"" + s_currentFileName + L"\" with you!"; + SetWindowText(hMessageEdit, defaultMessage.c_str()); + } + + // Initially disable the Select button until a contact is chosen + EnableWindow(GetDlgItem(hDlg, IDC_SELECT_CONTACT_BUTTON), FALSE); + + // Center the dialog on the parent + RECT rcParent, rcDlg; + HWND hParent = GetParent(hDlg); + if (hParent) + { + GetWindowRect(hParent, &rcParent); + GetWindowRect(hDlg, &rcDlg); + + int x = rcParent.left + (rcParent.right - rcParent.left - (rcDlg.right - rcDlg.left)) / 2; + int y = rcParent.top + (rcParent.bottom - rcParent.top - (rcDlg.bottom - rcDlg.top)) / 2; + + SetWindowPos(hDlg, HWND_TOP, x, y, 0, 0, SWP_NOSIZE); + } + + return TRUE; + } + + case WM_COMMAND: + switch (LOWORD(wParam)) + { + case IDC_CONTACT_SELECTION_LIST: + if (HIWORD(wParam) == LBN_SELCHANGE) + { + int selectedIndex = (int)SendDlgItemMessage(hDlg, IDC_CONTACT_SELECTION_LIST, LB_GETCURSEL, 0, 0); + HandleContactSelection(hDlg, selectedIndex); + } + else if (HIWORD(wParam) == LBN_DBLCLK) + { + // Double-click selects and closes dialog + OnSelectContact(hDlg); + } + break; + + case IDC_SELECT_CONTACT_BUTTON: + OnSelectContact(hDlg); + break; + + case IDC_CANCEL_SELECTION_BUTTON: + case IDCANCEL: + OnCancel(hDlg); + break; + + case IDOK: + OnSelectContact(hDlg); + break; + } + break; + + case WM_CLOSE: + OnCancel(hDlg); + break; + } + + return FALSE; +} + +void ContactSelectionDialog::InitializeContactList(HWND hListBox) +{ + // Set up the list box for contact display + SendMessage(hListBox, LB_RESETCONTENT, 0, 0); +} + +void ContactSelectionDialog::PopulateContactList(HWND hListBox) +{ + // Clear existing items + SendMessage(hListBox, LB_RESETCONTENT, 0, 0); + + // Ensure contacts are initialized + if (contacts.empty()) + { + InitializeContacts(); + } + + // Debug logging + OutputDebugStringW((L"ContactSelectionDialog: Populating list with " + std::to_wstring(contacts.size()) + L" contacts\n").c_str()); + + // Add all contacts to the list + for (size_t i = 0; i < contacts.size(); ++i) + { + const Contact& contact = contacts[i]; + + // Create display text with contact name and status + std::wstring displayText = contact.name + L" - " + contact.status; + if (!contact.isOnline) + { + displayText += L" (Offline)"; + } + + int itemIndex = (int)SendMessage(hListBox, LB_ADDSTRING, 0, (LPARAM)displayText.c_str()); + + // Store the contact index as item data + SendMessage(hListBox, LB_SETITEMDATA, itemIndex, (LPARAM)i); + + // Debug log each contact being added + OutputDebugStringW((L"ContactSelectionDialog: Added contact " + std::to_wstring(i) + L": " + contact.name + L"\n").c_str()); + } + + // If no contacts were added, add a placeholder + if (contacts.empty()) + { + SendMessage(hListBox, LB_ADDSTRING, 0, (LPARAM)L"No contacts available"); + OutputDebugStringW(L"ContactSelectionDialog: No contacts available - added placeholder\n"); + } + else + { + OutputDebugStringW((L"ContactSelectionDialog: Successfully populated " + std::to_wstring(contacts.size()) + L" contacts\n").c_str()); + } +} + +void ContactSelectionDialog::HandleContactSelection(HWND hDlg, int selectedIndex) +{ + if (selectedIndex != LB_ERR) + { + // Enable the Select button when a contact is selected + EnableWindow(GetDlgItem(hDlg, IDC_SELECT_CONTACT_BUTTON), TRUE); + + // Get the contact index from item data + HWND hListBox = GetDlgItem(hDlg, IDC_CONTACT_SELECTION_LIST); + int contactIndex = (int)SendMessage(hListBox, LB_GETITEMDATA, selectedIndex, 0); + + if (contactIndex >= 0 && contactIndex < (int)contacts.size()) + { + // Update the share message with the selected contact's name + const Contact& contact = contacts[contactIndex]; + std::wstring personalizedMessage = L"Hey " + contact.name + L"! I'm sharing \"" + s_currentFileName + L"\" with you."; + + HWND hMessageEdit = GetDlgItem(hDlg, IDC_SHARE_MESSAGE_EDIT); + if (hMessageEdit) + { + SetWindowText(hMessageEdit, personalizedMessage.c_str()); + } + } + } + else + { + // Disable the Select button when no contact is selected + EnableWindow(GetDlgItem(hDlg, IDC_SELECT_CONTACT_BUTTON), FALSE); + } +} + +void ContactSelectionDialog::OnSelectContact(HWND hDlg) +{ + HWND hListBox = GetDlgItem(hDlg, IDC_CONTACT_SELECTION_LIST); + int selectedIndex = (int)SendMessage(hListBox, LB_GETCURSEL, 0, 0); + + if (selectedIndex == LB_ERR) + { + MessageBox(hDlg, L"Please select a contact to share with.", L"No Contact Selected", MB_OK | MB_ICONWARNING); + return; + } + + // Check if contacts are available + if (contacts.empty()) + { + MessageBox(hDlg, L"No contacts are available. Please ensure the application is properly initialized.", L"No Contacts Available", MB_OK | MB_ICONWARNING); + return; + } + + // Get the contact index from item data + int contactIndex = (int)SendMessage(hListBox, LB_GETITEMDATA, selectedIndex, 0); + + // Debug logging + OutputDebugStringW((L"ContactSelectionDialog: Selected contact index: " + std::to_wstring(contactIndex) + L", total contacts: " + std::to_wstring(contacts.size()) + L"\n").c_str()); + + if (contactIndex >= 0 && contactIndex < (int)contacts.size()) + { + // Get the share message + HWND hMessageEdit = GetDlgItem(hDlg, IDC_SHARE_MESSAGE_EDIT); + WCHAR messageBuffer[512] = {0}; + if (hMessageEdit) + { + GetWindowText(hMessageEdit, messageBuffer, 512); + } + + // Store the result + s_dialogResult.contactIndex = contactIndex; + s_dialogResult.shareMessage = messageBuffer; + + // Debug logging + OutputDebugStringW((L"ContactSelectionDialog: Contact selected - " + contacts[contactIndex].name + L"\n").c_str()); + + // Close dialog with success + EndDialog(hDlg, IDOK); + } + else + { + MessageBox(hDlg, L"Invalid contact selection. Please try again.", L"Selection Error", MB_OK | MB_ICONERROR); + OutputDebugStringW((L"ContactSelectionDialog: Invalid contact index: " + std::to_wstring(contactIndex) + L"\n").c_str()); + } +} + +void ContactSelectionDialog::OnCancel(HWND hDlg) +{ + // Close dialog with cancel + EndDialog(hDlg, IDCANCEL); +} + +void ContactSelectionDialog::SetupDialogSizing(HWND hDlg) +{ + // Set dialog size (approximately 400x500 pixels) + SetWindowPos(hDlg, NULL, 0, 0, 420, 520, SWP_NOMOVE | SWP_NOZORDER); +} + +void ContactSelectionDialog::ApplyModernStyling(HWND hDlg) +{ + // Modern styling is optional for now - skip to avoid dependencies +} \ No newline at end of file diff --git a/Samples/SampleChatAppWithShare/SampleChatAppWithShare/ContactSelectionDialog.h b/Samples/SampleChatAppWithShare/SampleChatAppWithShare/ContactSelectionDialog.h new file mode 100644 index 0000000..fc06ffe --- /dev/null +++ b/Samples/SampleChatAppWithShare/SampleChatAppWithShare/ContactSelectionDialog.h @@ -0,0 +1,45 @@ +#pragma once + +#include +#include +#include +#include "ChatModels.h" + +// Contact Selection Dialog Manager +class ContactSelectionDialog +{ +public: + // Structure to hold the result of contact selection + struct SelectionResult + { + bool wasSelected; + int contactIndex; + std::wstring shareMessage; + std::wstring filePath; + std::wstring fileName; + + SelectionResult() : wasSelected(false), contactIndex(-1) {} + }; + + // Show the contact selection dialog + static SelectionResult ShowContactSelectionDialog(HWND hParent, const std::wstring& filePath, const std::wstring& fileName); + +private: + // Dialog procedure for contact selection + static INT_PTR CALLBACK ContactSelectionDlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam); + + // Helper functions + static void InitializeContactList(HWND hListBox); + static void PopulateContactList(HWND hListBox); + static void UpdateContactListDisplay(HWND hListBox); + static void HandleContactSelection(HWND hDlg, int selectedIndex); + static void OnSelectContact(HWND hDlg); + static void OnCancel(HWND hDlg); + static void SetupDialogSizing(HWND hDlg); + static void ApplyModernStyling(HWND hDlg); + + // Static data for dialog communication + static SelectionResult s_dialogResult; + static std::wstring s_currentFilePath; + static std::wstring s_currentFileName; +}; \ No newline at end of file diff --git a/Samples/SampleChatAppWithShare/SampleChatAppWithShare/FileManager.cpp b/Samples/SampleChatAppWithShare/SampleChatAppWithShare/FileManager.cpp index 4b9014d..31ace5f 100644 --- a/Samples/SampleChatAppWithShare/SampleChatAppWithShare/FileManager.cpp +++ b/Samples/SampleChatAppWithShare/SampleChatAppWithShare/FileManager.cpp @@ -9,19 +9,29 @@ // External declarations for UI window handles extern HWND hSharedFilesList; +extern HWND hContactsList; void ShareFile() { + // Check if a contact is selected first if (selectedContactIndex < 0) { - MessageBox(NULL, L"Please select a contact to share files with. ??", L"No Contact Selected", MB_OK | MB_ICONWARNING); + MessageBox(NULL, L"Please select a contact to share files with.", L"No Contact Selected", MB_OK | MB_ICONWARNING); return; } + // Get the main window handle (parent of the share file button) + HWND hMainWindow = GetParent(hSharedFilesList); + while (GetParent(hMainWindow)) + { + hMainWindow = GetParent(hMainWindow); + } + OPENFILENAME ofn; WCHAR szFile[260] = {0}; ZeroMemory(&ofn, sizeof(ofn)); ofn.lStructSize = sizeof(ofn); + ofn.hwndOwner = hMainWindow; // Set the main window as owner ofn.lpstrFile = szFile; ofn.nMaxFile = sizeof(szFile); ofn.lpstrFilter = L"All Files\0*.*\0?? Text Files\0*.TXT\0?? Document Files\0*.DOC;*.DOCX\0??? Image Files\0*.BMP;*.JPG;*.PNG;*.GIF\0?? PDF Files\0*.PDF\0?? Excel Files\0*.XLS;*.XLSX\0"; @@ -32,14 +42,18 @@ void ShareFile() ofn.lpstrTitle = L"Select File to Share"; ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_EXPLORER; - if (GetOpenFileName(&ofn)) { + if (GetOpenFileName(&ofn)) + { // Extract file name from full path std::wstring fullPath(szFile); size_t lastSlash = fullPath.find_last_of(L"\\"); std::wstring fileName; - if (lastSlash != std::wstring::npos) { + if (lastSlash != std::wstring::npos) + { fileName = fullPath.substr(lastSlash + 1); - } else { + } + else + { fileName = fullPath; } @@ -50,11 +64,28 @@ void ShareFile() newFile.sharedBy = L"You"; GetSystemTime(&newFile.timeShared); - // Add to chat and shared files - AddSharedFileToChat(newFile, true); - - // Simulate auto-reply from contact acknowledging the file - SetTimer(GetParent(hSharedFilesList), 2, 1500, NULL); + // Add file to the currently selected contact's shared files + if (selectedContactIndex >= 0 && selectedContactIndex < (int)contacts.size()) + { + contacts[selectedContactIndex].sharedFiles.push_back(newFile); + + // Add file sharing notification to chat + std::wstring fileShareMsg = L"?? Shared file: " + fileName; + AddMessageToChat(fileShareMsg, true); + + // Update UI + AddSharedFileToChat(newFile, true); + + // Update contacts list display to show the shared activity + InvalidateRect(hContactsList, NULL, TRUE); + + // Show success message with current contact + std::wstring successMsg = L"File \"" + fileName + L"\" has been shared with " + contacts[selectedContactIndex].name + L"!"; + MessageBox(hMainWindow, successMsg.c_str(), L"File Shared Successfully", MB_OK | MB_ICONINFORMATION); + + // Simulate auto-reply from contact acknowledging the file + SetTimer(hMainWindow, 2, 2000, NULL); + } } } @@ -64,99 +95,60 @@ void AddSharedFileToChat(const SharedFile& file, bool isOutgoing) if (!contact) return; std::wstring sharer = isOutgoing ? L"You" : contact->name; - std::wstring formattedMessage = sharer + L" shared: " + file.fileName; - contact->messages.push_back(formattedMessage); + // Format file sharing message with timestamp + SYSTEMTIME st; + GetLocalTime(&st); + WCHAR timeStr[50]; + swprintf_s(timeStr, 50, L"[%02d:%02d] ", st.wHour, st.wMinute); + + std::wstring shareMessage = sharer + L" shared: " + file.fileName + L" ??"; + contact->messages.push_back(shareMessage); + + // Update last message preview + contact->lastMessage = L"?? " + file.fileName; // Add to shared files list - if (isOutgoing) { - SharedFile newFile = file; - newFile.sharedBy = L"You"; - contact->sharedFiles.push_back(newFile); - } + contact->sharedFiles.push_back(file); - // Update both chat display and shared files list + // Refresh UI LoadContactChat(selectedContactIndex); + UpdateSharedFilesList(); } -void UpdateSharedFilesList() +void OpenSharedFile(int fileIndex) { Contact* contact = GetSelectedContact(); - if (!contact) return; + if (!contact || fileIndex < 0 || fileIndex >= (int)contact->sharedFiles.size()) { + return; + } - // Clear the list - ::SendMessage(hSharedFilesList, LB_RESETCONTENT, 0, 0); + const SharedFile& file = contact->sharedFiles[fileIndex]; - // Add shared files with file type icons - for (const auto& file : contact->sharedFiles) { - std::wstring icon = GetFileExtensionIcon(file.fileName); - std::wstring displayText = icon + L" " + file.fileName; - displayText += L"\n ?? " + file.sharedBy; - ::SendMessage(hSharedFilesList, LB_ADDSTRING, 0, (LPARAM)displayText.c_str()); + // Try to open the file with the default application + HINSTANCE result = ShellExecute(NULL, L"open", file.filePath.c_str(), NULL, NULL, SW_SHOWNORMAL); + + if ((intptr_t)result <= 32) { + // If opening failed, show file location in explorer + std::wstring explorerCmd = L"/select,\"" + file.filePath + L"\""; + ShellExecute(NULL, L"open", L"explorer.exe", explorerCmd.c_str(), NULL, SW_SHOWNORMAL); } } -void OpenSharedFile(int fileIndex) +void UpdateSharedFilesList() { - Contact* contact = GetSelectedContact(); - if (!contact || fileIndex < 0 || fileIndex >= (int)contact->sharedFiles.size()) return; - - const SharedFile& file = contact->sharedFiles[fileIndex]; + if (!hSharedFilesList) return; - // Enhanced file info display with emojis and better formatting - std::wstring message = L"File Information\n\n"; - message += L"?? Name: " + file.fileName + L"\n"; - message += L"?? Path: " + file.filePath + L"\n"; - message += L"?? Shared by: " + file.sharedBy + L"\n"; + Contact* contact = GetSelectedContact(); - WCHAR timeStr[100]; - swprintf_s(timeStr, 100, L"?? Shared on: %02d/%02d/%04d at %02d:%02d", - file.timeShared.wMonth, file.timeShared.wDay, file.timeShared.wYear, - file.timeShared.wHour, file.timeShared.wMinute); - message += timeStr; - message += L"\n\n?? Tip: In a real application, this file would open automatically!"; + // Clear the list + SendMessage(hSharedFilesList, LB_RESETCONTENT, 0, 0); - MessageBox(NULL, message.c_str(), L"Shared File Information", MB_OK | MB_ICONINFORMATION); + if (!contact) return; - // In a real application, you would use ShellExecute to open the file: - // ShellExecute(NULL, L"open", file.filePath.c_str(), NULL, NULL, SW_SHOWNORMAL); -} - -std::wstring GetFileExtensionIcon(const std::wstring& filePath) -{ - size_t dotPos = filePath.find_last_of(L"."); - if (dotPos != std::wstring::npos) { - std::wstring ext = filePath.substr(dotPos + 1); - std::transform(ext.begin(), ext.end(), ext.begin(), ::towlower); - - if (ext == L"txt") return L"??"; - if (ext == L"doc" || ext == L"docx") return L"??"; - if (ext == L"pdf") return L"??"; - if (ext == L"xls" || ext == L"xlsx") return L"??"; - if (ext == L"jpg" || ext == L"png" || ext == L"gif" || ext == L"bmp") return L"???"; - if (ext == L"mp3" || ext == L"wav") return L"??"; - if (ext == L"mp4" || ext == L"avi") return L"??"; - if (ext == L"zip" || ext == L"rar") return L"???"; - if (ext == L"exe" || ext == L"msi") return L"??"; - } - return L"??"; -} - -std::wstring FormatFileSize(DWORD fileSize) -{ - const DWORD KB = 1024; - const DWORD MB = KB * 1024; - const DWORD GB = MB * 1024; - - WCHAR buffer[50]; - if (fileSize >= GB) { - swprintf_s(buffer, 50, L"%.1f GB", (double)fileSize / GB); - } else if (fileSize >= MB) { - swprintf_s(buffer, 50, L"%.1f MB", (double)fileSize / MB); - } else if (fileSize >= KB) { - swprintf_s(buffer, 50, L"%.1f KB", (double)fileSize / KB); - } else { - swprintf_s(buffer, 50, L"%d bytes", fileSize); + // Add shared files to the list + for (const auto& file : contact->sharedFiles) { + std::wstring displayText = file.fileName + L" (shared by " + file.sharedBy + L")"; + SendMessage(hSharedFilesList, LB_ADDSTRING, 0, (LPARAM)displayText.c_str()); } - return std::wstring(buffer); } \ No newline at end of file diff --git a/Samples/SampleChatAppWithShare/SampleChatAppWithShare/Resource.h b/Samples/SampleChatAppWithShare/SampleChatAppWithShare/Resource.h index 04a9f8f..e41d7d3 100644 --- a/Samples/SampleChatAppWithShare/SampleChatAppWithShare/Resource.h +++ b/Samples/SampleChatAppWithShare/SampleChatAppWithShare/Resource.h @@ -7,8 +7,9 @@ #define IDR_MAINFRAME 128 #define IDD_SAMPLECHATAPPWITHSHARE_DIALOG 102 #define IDD_ABOUTBOX 103 -#define IDM_ABOUT 104 -#define IDM_EXIT 105 +#define IDD_CONTACT_SELECTION 104 +#define IDM_ABOUT 105 +#define IDM_EXIT 106 #define IDI_SAMPLECHATAPPWITHSHARE 107 #define IDI_SMALL 108 #define IDC_SAMPLECHATAPPWITHSHARE 109 @@ -23,6 +24,13 @@ #define IDC_SHARE_FILE_BUTTON 1006 #define IDC_SHARED_FILES_LIST 1007 +// Contact Selection Dialog Controls +#define IDC_CONTACT_SELECTION_LIST 1008 +#define IDC_SELECT_CONTACT_BUTTON 1009 +#define IDC_CANCEL_SELECTION_BUTTON 1010 +#define IDC_SHARE_MESSAGE_EDIT 1011 +#define IDC_DIALOG_TITLE 1012 + #ifndef IDC_STATIC #define IDC_STATIC -1 #endif @@ -34,7 +42,7 @@ #define _APS_NO_MFC 130 #define _APS_NEXT_RESOURCE_VALUE 129 #define _APS_NEXT_COMMAND_VALUE 32771 -#define _APS_NEXT_CONTROL_VALUE 1008 +#define _APS_NEXT_CONTROL_VALUE 1013 #define _APS_NEXT_SYMED_VALUE 110 #endif #endif diff --git a/Samples/SampleChatAppWithShare/SampleChatAppWithShare/SampleChatAppWithShare.rc b/Samples/SampleChatAppWithShare/SampleChatAppWithShare/SampleChatAppWithShare.rc index 2a71b01d2eb77aa6eea69a359bcd7b88b4930214..966facaf26def5e6f0d0f872d58177bdfd2706d1 100644 GIT binary patch delta 1309 zcma)6&1w@-6#lTbq@_*Lh%KQAV|Im>WHKogv5Rz!H4vvYld8KgjnxJdBR1j>!q^v3 z|cQWNp1u2)@Ip>~x&iT%F&&l`IUmKs_Wf$wX$<>C7 zO?YUbN~uNNM-2`BM5~KUeVx9Vsi4HQ1Pc{hLrMLa{<7j@4*{NIm#4STMNdo1)O<>! zbugsnVt}WV0@2m+jFXSz#1W&oCA@;IX44DmeWq|X86jYljv0mekh+C^1H+KAV0mmFaBiY9jpZ~2R@w|VMoEX^AP~U7mLtXQ+#MEoG^LbbdZ! zEIBJ=?y#R7Tv&@)F^Ba0e_cOk{V@)_vUA08hkY&5W%VXEr%ylT>~UeIq>fYbhex@; FsS^-F)5`z= delta 21 dcmaFhe#Lx4gTQ7l@ffDbYdE + @@ -199,6 +200,7 @@ + @@ -217,6 +219,12 @@ + + + + + + diff --git a/Samples/SampleChatAppWithShare/SampleChatAppWithShare/ShareTargetManager.cpp b/Samples/SampleChatAppWithShare/SampleChatAppWithShare/ShareTargetManager.cpp index 178160d..1400d14 100644 --- a/Samples/SampleChatAppWithShare/SampleChatAppWithShare/ShareTargetManager.cpp +++ b/Samples/SampleChatAppWithShare/SampleChatAppWithShare/ShareTargetManager.cpp @@ -1,5 +1,9 @@ #include "ShareTargetManager.h" #include "PackageIdentity.h" +#include "ContactSelectionDialog.h" +#include "ChatManager.h" +#include "ChatModels.h" +#include "FileManager.h" #include "framework.h" #include @@ -81,13 +85,26 @@ bool ShareTargetManager::ProcessActivationArgs() { LogShareInfo(L"? Share Target activation detected!"); - // Process the share target directly here (same as original code) + // Ensure contacts are initialized (critical for share target scenarios) + if (contacts.empty()) + { + LogShareInfo(L"Initializing contacts for share target scenario..."); + InitializeContacts(); + } + + // Process the share target directly here auto shareArgs = activationArgs.as(); auto shareOperation = shareArgs.ShareOperation(); auto data = shareOperation.Data(); - std::wstring shareInfo = L"Share Target Activated!\n\n"; - + // Extract shared content information for the contact selection dialog + std::wstring sharedContentSummary = L"Shared Content"; + std::wstring sharedFiles; + bool hasFiles = false; + + // Collect information about what's being shared + std::vector sharedItems; + // Check for different data formats if (data.Contains(winrt::Windows::ApplicationModel::DataTransfer::StandardDataFormats::Text())) { @@ -95,12 +112,12 @@ bool ShareTargetManager::ProcessActivationArgs() { auto textAsync = data.GetTextAsync(); auto text = textAsync.get(); - shareInfo += L"?? Text: " + std::wstring(text.c_str()) + L"\n\n"; + sharedItems.push_back(L"Text: " + std::wstring(text.c_str())); LogShareInfo(L"Received shared text."); } catch (...) { - shareInfo += L"?? Text: [Error retrieving text]\n\n"; + sharedItems.push_back(L"Text: [Error retrieving text]"); LogShareError(L"Error retrieving shared text."); } } @@ -111,12 +128,12 @@ bool ShareTargetManager::ProcessActivationArgs() { auto webLinkAsync = data.GetWebLinkAsync(); auto webLink = webLinkAsync.get(); - shareInfo += L"?? Web Link: " + std::wstring(webLink.ToString().c_str()) + L"\n\n"; + sharedItems.push_back(L"Web Link: " + std::wstring(webLink.ToString().c_str())); LogShareInfo(L"Received shared web link."); } catch (...) { - shareInfo += L"?? Web Link: [Error retrieving web link]\n\n"; + sharedItems.push_back(L"Web Link: [Error retrieving web link]"); LogShareError(L"Error retrieving shared web link."); } } @@ -127,12 +144,12 @@ bool ShareTargetManager::ProcessActivationArgs() { auto bitmapAsync = data.GetBitmapAsync(); auto bitmapRef = bitmapAsync.get(); - shareInfo += L"??? Bitmap: Received image content\n\n"; + sharedItems.push_back(L"Image/Bitmap content"); LogShareInfo(L"Received shared bitmap."); } catch (...) { - shareInfo += L"??? Bitmap: [Error retrieving bitmap]\n\n"; + sharedItems.push_back(L"Image: [Error retrieving image]"); LogShareError(L"Error retrieving shared bitmap."); } } @@ -143,27 +160,134 @@ bool ShareTargetManager::ProcessActivationArgs() { auto storageItemsAsync = data.GetStorageItemsAsync(); auto storageItems = storageItemsAsync.get(); - shareInfo += L"?? Files: " + std::to_wstring(storageItems.Size()) + L" file(s) shared\n"; + + hasFiles = true; + std::wstring filesInfo = std::to_wstring(storageItems.Size()) + L" file(s)"; + + if (storageItems.Size() == 1) + { + auto item = storageItems.GetAt(0); + sharedFiles = item.Name().c_str(); + sharedContentSummary = sharedFiles; + } + else if (storageItems.Size() > 1) + { + sharedFiles = L"Multiple Files (" + std::to_wstring(storageItems.Size()) + L")"; + sharedContentSummary = sharedFiles; + } for (uint32_t i = 0; i < storageItems.Size(); i++) { auto item = storageItems.GetAt(i); - shareInfo += L" - " + std::wstring(item.Name().c_str()) + L"\n"; + filesInfo += L"\n - " + std::wstring(item.Name().c_str()); } - shareInfo += L"\n"; + + sharedItems.push_back(filesInfo); LogShareInfo(L"Received shared files."); } catch (...) { - shareInfo += L"?? Files: [Error retrieving files]\n\n"; + sharedItems.push_back(L"Files: [Error retrieving files]"); LogShareError(L"Error retrieving shared files."); } } - // Show the share information - MessageBoxW(nullptr, shareInfo.c_str(), L"Windows Share Target", MB_OK | MB_ICONINFORMATION); + // If no specific content type found, use generic description + if (sharedItems.empty()) + { + sharedContentSummary = L"Shared Content"; + sharedItems.push_back(L"Unknown content type"); + } + + // Get the main window handle for dialog parent + HWND hMainWindow = GetActiveWindow(); + if (!hMainWindow) + { + hMainWindow = GetForegroundWindow(); + } + if (!hMainWindow) + { + // Try to find the main chat application window + hMainWindow = FindWindow(NULL, L"Chat Application"); + } + + // Log the number of contacts available for debugging + LogShareInfo(L"Available contacts: " + std::to_wstring(contacts.size())); + + // Show contact selection dialog for the shared content + ContactSelectionDialog::SelectionResult result = + ContactSelectionDialog::ShowContactSelectionDialog(hMainWindow, L"", sharedContentSummary); + + if (result.wasSelected) + { + // User selected a contact - add the shared content to that contact's chat + if (result.contactIndex >= 0 && result.contactIndex < (int)contacts.size()) + { + int previousSelection = selectedContactIndex; + selectedContactIndex = result.contactIndex; + + // Add the custom share message if provided + if (!result.shareMessage.empty()) + { + AddMessageToChat(result.shareMessage, true); + } + + // Add shared content messages to the chat + for (const auto& item : sharedItems) + { + std::wstring shareMsg = L"?? Received via Share: " + item; + AddMessageToChat(shareMsg, false); // Mark as incoming since it's from external source + } + + // If files were shared, add them to the contact's shared files list + if (hasFiles && data.Contains(winrt::Windows::ApplicationModel::DataTransfer::StandardDataFormats::StorageItems())) + { + try + { + auto storageItemsAsync = data.GetStorageItemsAsync(); + auto storageItems = storageItemsAsync.get(); + + for (uint32_t i = 0; i < storageItems.Size(); i++) + { + auto item = storageItems.GetAt(i); + + // Create shared file entry + SharedFile newFile; + newFile.fileName = item.Name().c_str(); + newFile.filePath = item.Path().c_str(); // Get full path if available + newFile.sharedBy = L"External Share"; + GetSystemTime(&newFile.timeShared); + + contacts[result.contactIndex].sharedFiles.push_back(newFile); + } + + // Update shared files UI + UpdateSharedFilesList(); + } + catch (...) + { + LogShareError(L"Error adding shared files to contact."); + } + } + + // Update UI to show the chat with the selected contact + LoadContactChat(result.contactIndex); + + // Show success message + std::wstring successMsg = L"Shared content has been added to your conversation with " + contacts[result.contactIndex].name + L"!"; + MessageBoxW(hMainWindow, successMsg.c_str(), L"Content Shared Successfully", MB_OK | MB_ICONINFORMATION); + + LogShareInfo(L"Share target content added to contact: " + contacts[result.contactIndex].name); + } + } + else + { + // User cancelled - show a brief message + MessageBoxW(hMainWindow, L"Share operation was cancelled.", L"Share Cancelled", MB_OK | MB_ICONINFORMATION); + LogShareInfo(L"Share target operation cancelled by user."); + } - // Report completion + // Report completion to Windows shareOperation.ReportCompleted(); LogShareInfo(L"Share target processing completed successfully."); diff --git a/Samples/SampleChatAppWithShare/SampleChatAppWithShare/WindowProcs.cpp b/Samples/SampleChatAppWithShare/SampleChatAppWithShare/WindowProcs.cpp index 8a0658c..15fb282 100644 --- a/Samples/SampleChatAppWithShare/SampleChatAppWithShare/WindowProcs.cpp +++ b/Samples/SampleChatAppWithShare/SampleChatAppWithShare/WindowProcs.cpp @@ -108,13 +108,27 @@ LRESULT CALLBACK ModernListBoxProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lP // Fill background FillRect(hdc, &clientRect, hBrushSurface); - int itemCount = contacts.size(); - int selectedIndex = ::SendMessage(hWnd, LB_GETCURSEL, 0, 0); + int itemCount = (int)contacts.size(); + int selectedIndex = (int)::SendMessage(hWnd, LB_GETCURSEL, 0, 0); - for (int i = 0; i < itemCount; i++) + // Get the first visible item index to handle scrolling correctly + int topIndex = (int)::SendMessage(hWnd, LB_GETTOPINDEX, 0, 0); + + // Calculate how many items can be visible + int visibleItemCount = (clientRect.bottom / CONTACT_ITEM_HEIGHT) + 1; + + // Draw only the visible items + for (int visiblePos = 0; visiblePos < visibleItemCount; visiblePos++) { - RECT itemRect = {0, i * CONTACT_ITEM_HEIGHT, clientRect.right, (i + 1) * CONTACT_ITEM_HEIGHT}; - DrawContactItem(hdc, itemRect, contacts[i], i == selectedIndex); + int actualIndex = topIndex + visiblePos; + + // Stop if we've reached the end of the contact list + if (actualIndex >= itemCount) break; + + RECT itemRect = {0, visiblePos * CONTACT_ITEM_HEIGHT, clientRect.right, (visiblePos + 1) * CONTACT_ITEM_HEIGHT}; + + // Draw the contact that should be visible at this position + DrawContactItem(hdc, itemRect, contacts[actualIndex], actualIndex == selectedIndex); } EndPaint(hWnd, &ps); From d9d09cf9968e51ba7d5ecc3f173c9318daffae1e Mon Sep 17 00:00:00 2001 From: Ravish Roshan Date: Wed, 24 Sep 2025 06:42:10 +0530 Subject: [PATCH 08/20] No main windows in sharing flow --- .../SampleChatAppWithShare.cpp | 64 ++++++++++++++--- .../SampleChatAppWithShare.vcxproj | 3 + .../ShareTargetManager.cpp | 70 +++++++++++++++---- .../SampleChatAppWithShare/WindowProcs.cpp | 27 +++++++ 4 files changed, 139 insertions(+), 25 deletions(-) diff --git a/Samples/SampleChatAppWithShare/SampleChatAppWithShare/SampleChatAppWithShare.cpp b/Samples/SampleChatAppWithShare/SampleChatAppWithShare/SampleChatAppWithShare.cpp index 23ddb2d..ff6cdcd 100644 --- a/Samples/SampleChatAppWithShare/SampleChatAppWithShare/SampleChatAppWithShare.cpp +++ b/Samples/SampleChatAppWithShare/SampleChatAppWithShare/SampleChatAppWithShare.cpp @@ -38,6 +38,9 @@ HINSTANCE hInst; // current instance WCHAR szTitle[MAX_LOADSTRING]; // The title bar text WCHAR szWindowClass[MAX_LOADSTRING]; // the main window class name +// Global flag to track if we're in share-only mode +bool g_isShareOnlyMode = false; + // Forward declarations of functions included in this code module: ATOM MyRegisterClass(HINSTANCE hInstance); BOOL InitInstance(HINSTANCE, int); @@ -75,6 +78,16 @@ int APIENTRY wWinMain(_In_ HINSTANCE hInstance, // Initialize Share Target Manager ShareTargetManager::Initialize(); + // Note: Don't process share target activation here - UI isn't created yet + // This will be handled after UI creation in WndProc WM_CREATE + + // Check if this is a share target activation to determine if we need the main window + if (g_isRunningWithIdentity && ShareTargetManager::IsShareTargetActivation()) + { + g_isShareOnlyMode = true; + OutputDebugStringW(L"ChatApp: Running in share-only mode - main window will not be created\n"); + } + if (g_isSparsePackageSupported && !g_isRunningWithIdentity) { std::wstring executableDir = GetExecutableDirectory(); @@ -127,28 +140,51 @@ int APIENTRY wWinMain(_In_ HINSTANCE hInstance, // Initialize global strings LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING); LoadStringW(hInstance, IDC_SAMPLECHATAPPWITHSHARE, szWindowClass, MAX_LOADSTRING); - MyRegisterClass(hInstance); - - // Initialize modern UI - InitializeModernUI(); + + if (!g_isShareOnlyMode) + { + // Only register window class if we're not in share-only mode + MyRegisterClass(hInstance); + + // Initialize modern UI + InitializeModernUI(); + } - // Initialize dummy contacts + // Initialize dummy contacts (needed for share target) InitializeContacts(); - // Perform application initialization: - if (!InitInstance (hInstance, nCmdShow)) + if (g_isShareOnlyMode) + { + // In share-only mode, process the share target directly without creating a window + OutputDebugStringW(L"ChatApp: Processing share target in share-only mode\n"); + ShareTargetManager::ProcessActivationArgs(); + } + else { - return FALSE; + // Normal mode: create and show the main window + if (!InitInstance(hInstance, nCmdShow)) + { + return FALSE; + } } - HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_SAMPLECHATAPPWITHSHARE)); + HACCEL hAccelTable = nullptr; + if (!g_isShareOnlyMode) + { + hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_SAMPLECHATAPPWITHSHARE)); + } MSG msg; // Main message loop: while (GetMessage(&msg, nullptr, 0, 0)) { - if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)) + if (!g_isShareOnlyMode && !TranslateAccelerator(msg.hwnd, hAccelTable, &msg)) + { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + else if (g_isShareOnlyMode) { TranslateMessage(&msg); DispatchMessage(&msg); @@ -224,6 +260,12 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { case WM_CREATE: CreateChatUI(hWnd); + + // Process share target activation after UI is created + if (g_isRunningWithIdentity) + { + ShareTargetManager::ProcessActivationArgs(); + } break; case WM_SIZE: @@ -366,7 +408,7 @@ INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) try { winrt::check_hresult(S_OK); - aboutText += L"?? WinRT Status: ? Initialized and Working\n"; + aboutText += L"? WinRT Status: ? Initialized and Working\n"; if (g_isRunningWithIdentity) { diff --git a/Samples/SampleChatAppWithShare/SampleChatAppWithShare/SampleChatAppWithShare.vcxproj b/Samples/SampleChatAppWithShare/SampleChatAppWithShare/SampleChatAppWithShare.vcxproj index fd945d9..131e855 100644 --- a/Samples/SampleChatAppWithShare/SampleChatAppWithShare/SampleChatAppWithShare.vcxproj +++ b/Samples/SampleChatAppWithShare/SampleChatAppWithShare/SampleChatAppWithShare.vcxproj @@ -222,8 +222,11 @@ + + + diff --git a/Samples/SampleChatAppWithShare/SampleChatAppWithShare/ShareTargetManager.cpp b/Samples/SampleChatAppWithShare/SampleChatAppWithShare/ShareTargetManager.cpp index 1400d14..82131dc 100644 --- a/Samples/SampleChatAppWithShare/SampleChatAppWithShare/ShareTargetManager.cpp +++ b/Samples/SampleChatAppWithShare/SampleChatAppWithShare/ShareTargetManager.cpp @@ -78,12 +78,20 @@ bool ShareTargetManager::ProcessActivationArgs() if (!IsShareTargetAvailable()) return false; + // Add a static flag to prevent multiple processing + static bool s_alreadyProcessed = false; + if (s_alreadyProcessed) { + LogShareInfo(L"Share target already processed - skipping duplicate call"); + return false; + } + try { auto activationArgs = winrt::Windows::ApplicationModel::AppInstance::GetActivatedEventArgs(); if (activationArgs && activationArgs.Kind() == winrt::Windows::ApplicationModel::Activation::ActivationKind::ShareTarget) { LogShareInfo(L"? Share Target activation detected!"); + s_alreadyProcessed = true; // Mark as processed // Ensure contacts are initialized (critical for share target scenarios) if (contacts.empty()) @@ -226,17 +234,34 @@ bool ShareTargetManager::ProcessActivationArgs() int previousSelection = selectedContactIndex; selectedContactIndex = result.contactIndex; + LogShareInfo(L"Setting selectedContactIndex to: " + std::to_wstring(result.contactIndex)); + LogShareInfo(L"Selected contact name: " + contacts[result.contactIndex].name); + + // Add messages directly to the selected contact instead of relying on selectedContactIndex + Contact& selectedContact = contacts[result.contactIndex]; + // Add the custom share message if provided if (!result.shareMessage.empty()) { - AddMessageToChat(result.shareMessage, true); + std::wstring formattedShareMessage = L"You: " + result.shareMessage + L" ??"; + selectedContact.messages.push_back(formattedShareMessage); + LogShareInfo(L"Added share message: " + result.shareMessage); } // Add shared content messages to the chat for (const auto& item : sharedItems) { std::wstring shareMsg = L"?? Received via Share: " + item; - AddMessageToChat(shareMsg, false); // Mark as incoming since it's from external source + std::wstring formattedMessage = selectedContact.name + L": " + shareMsg; + selectedContact.messages.push_back(formattedMessage); + LogShareInfo(L"Added shared content message: " + shareMsg); + } + + // Update the last message preview for this contact + if (!sharedItems.empty()) + { + std::wstring lastMsg = L"?? Received via Share: " + sharedItems[0]; + selectedContact.lastMessage = lastMsg.length() > 50 ? lastMsg.substr(0, 47) + L"..." : lastMsg; } // If files were shared, add them to the contact's shared files list @@ -258,11 +283,9 @@ bool ShareTargetManager::ProcessActivationArgs() newFile.sharedBy = L"External Share"; GetSystemTime(&newFile.timeShared); - contacts[result.contactIndex].sharedFiles.push_back(newFile); + selectedContact.sharedFiles.push_back(newFile); + LogShareInfo(L"Added shared file: " + newFile.fileName); } - - // Update shared files UI - UpdateSharedFilesList(); } catch (...) { @@ -270,21 +293,38 @@ bool ShareTargetManager::ProcessActivationArgs() } } - // Update UI to show the chat with the selected contact - LoadContactChat(result.contactIndex); - - // Show success message - std::wstring successMsg = L"Shared content has been added to your conversation with " + contacts[result.contactIndex].name + L"!"; - MessageBoxW(hMainWindow, successMsg.c_str(), L"Content Shared Successfully", MB_OK | MB_ICONINFORMATION); + // Show success message and exit the application + std::wstring successMsg = L"Content has been shared successfully with " + contacts[result.contactIndex].name + L"!\n\nThe application will now close."; + MessageBoxW(hMainWindow, successMsg.c_str(), L"Sharing Complete", MB_OK | MB_ICONINFORMATION); LogShareInfo(L"Share target content added to contact: " + contacts[result.contactIndex].name); + LogShareInfo(L"Selected contact index set to: " + std::to_wstring(result.contactIndex)); + LogShareInfo(L"Contact now has " + std::to_wstring(selectedContact.messages.size()) + L" messages"); + + // Report completion to Windows + shareOperation.ReportCompleted(); + + LogShareInfo(L"Share target processing completed successfully. Exiting application."); + + // Exit the application after successful sharing + PostQuitMessage(0); + return true; } } else { - // User cancelled - show a brief message - MessageBoxW(hMainWindow, L"Share operation was cancelled.", L"Share Cancelled", MB_OK | MB_ICONINFORMATION); + // User cancelled - show a brief message and exit + MessageBoxW(hMainWindow, L"Share operation was cancelled.\n\nThe application will now close.", L"Share Cancelled", MB_OK | MB_ICONINFORMATION); LogShareInfo(L"Share target operation cancelled by user."); + + // Report completion to Windows + shareOperation.ReportCompleted(); + + LogShareInfo(L"Share target processing cancelled. Exiting application."); + + // Exit the application after cancellation + PostQuitMessage(0); + return true; } // Report completion to Windows @@ -301,6 +341,7 @@ bool ShareTargetManager::ProcessActivationArgs() } catch (winrt::hresult_error const& ex) { + s_alreadyProcessed = true; // Mark as processed even on error to prevent retries std::wstring error = L"Error checking activation args: " + std::wstring(ex.message().c_str()) + L" (HRESULT: 0x" + std::to_wstring(static_cast(ex.code())) + L")"; LogShareError(error); @@ -309,6 +350,7 @@ bool ShareTargetManager::ProcessActivationArgs() } catch (...) { + s_alreadyProcessed = true; // Mark as processed even on error to prevent retries LogShareError(L"Unknown error checking activation arguments."); MessageBoxW(nullptr, L"Unknown error processing shared content", L"Share Target Error", MB_OK | MB_ICONERROR); return false; diff --git a/Samples/SampleChatAppWithShare/SampleChatAppWithShare/WindowProcs.cpp b/Samples/SampleChatAppWithShare/SampleChatAppWithShare/WindowProcs.cpp index 15fb282..ce00862 100644 --- a/Samples/SampleChatAppWithShare/SampleChatAppWithShare/WindowProcs.cpp +++ b/Samples/SampleChatAppWithShare/SampleChatAppWithShare/WindowProcs.cpp @@ -96,6 +96,33 @@ LRESULT CALLBACK ModernListBoxProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lP { switch (msg) { + case WM_LBUTTONDOWN: + if (hWnd == hContactsList) + { + // Handle mouse click to ensure proper contact selection with scrolling + POINT pt = { LOWORD(lParam), HIWORD(lParam) }; + + // Calculate which contact was clicked based on the scroll position + int topIndex = (int)::SendMessage(hWnd, LB_GETTOPINDEX, 0, 0); + int clickedVisiblePos = pt.y / CONTACT_ITEM_HEIGHT; + int actualContactIndex = topIndex + clickedVisiblePos; + + // Ensure the clicked contact is valid + if (actualContactIndex >= 0 && actualContactIndex < (int)contacts.size()) + { + // Set the correct selection in the listbox + ::SendMessage(hWnd, LB_SETCURSEL, actualContactIndex, 0); + + // Trigger the selection change event to update the UI + HWND hParent = GetParent(hWnd); + int controlId = GetDlgCtrlID(hWnd); + ::SendMessage(hParent, WM_COMMAND, MAKEWPARAM(controlId, LBN_SELCHANGE), (LPARAM)hWnd); + + return 0; // We handled the click + } + } + break; + case WM_PAINT: if (hWnd == hContactsList) { From b9aed2b89294289d4e2486817a7a9af47edb0dc6 Mon Sep 17 00:00:00 2001 From: Ravish Roshan Date: Thu, 6 Nov 2025 07:51:28 +0530 Subject: [PATCH 09/20] Added Readme --- Samples/SampleChatAppWithShare/README.md | 39 ++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 Samples/SampleChatAppWithShare/README.md diff --git a/Samples/SampleChatAppWithShare/README.md b/Samples/SampleChatAppWithShare/README.md new file mode 100644 index 0000000..15680b1 --- /dev/null +++ b/Samples/SampleChatAppWithShare/README.md @@ -0,0 +1,39 @@ +This repo contains a sample C++ application demonstrating how to use [Signed Sparse Packages](https://aka.ms/sparsepkgblog) to give a non-packaged desktop app access to new Windows APIs and features. + +## Instructions + +You can learn more about Signed Sparse Packages and Identity, Registration & Activation of Win32 apps in this [blogpost](https://aka.ms/sparsepkgblog) and in the [documentation](https://aka.ms/sparsepkgdocs). + +### Requirements + +1. Windows SDK version 10.0.19000.0 + +2. Windows OS version 10.0.19000.0 + +3. Microsoft Visual C++ Redistributables + +### PackageWithExternalLocationCppApp + +A non-package native Windows desktop Win32 GUI application written in C++. It installs a Signed Sparse Package and uses it to act as a Share Target. + +* Registration of a Signed Sparse Package and handling of Shared photos happens in WinMain.cpp. + +* Files to package and sign to create a Sparse Package for use with the app are located in the PackageWithExternalLocationCppSample directory inside PackageWithExternalLocation sample. + + +### Building and running the sample + +1. Make sure your machine has Developer Mode turned on. +2. Retarget the solution to the SDK version on your machine – Right click -> Retarget solution. +3. Add a project reference to the Windows.winmd file at "C:\Program Files (x86)\Windows Kits\10\UnionMetadata\\\Windows.winmd". (Right click PhotoStoreDemo project | Add | Reference| Browse | All files | Windows.winmd) +4. Update the Publisher value in the AppxManifest.xml file and in SampleChatAppWithShare.exe.manifest to match the Publisher value in your cert. If you need to create a cert for signing have a look at [Creating an app package signing certificate](https://docs.microsoft.com/en-us/windows/win32/appxpkg/how-to-create-a-package-signing-certificate). +5. Install your cert on the machine +6. Create a Sparse Package by packaging the updated contents of PackageWithExternalLocationCppSample using [App Packager](https://docs.microsoft.com/en-us/windows/win32/appxpkg/make-appx-package--makeappx-exe-) (MakeAppx.exe) and specifying the **/nv** flag. For example: MakeAppx.exe pack /d \ /p \ mypackage.msix /nv +7. Sign the new Sparse Package. See [Signing an app package using SignTool](https://docs.microsoft.com/en-us/windows/win32/appxpkg/how-to-sign-a-package-using-signtool) or you can also use [Device Guard Signing](https://docs.microsoft.com/en-us/microsoft-store/device-guard-signing-portal). +8. In the WinMain method (in WinMain.cpp) update the value of **externalLocation** to match the output location of your VS Build binaries and the value of **packagePath** to match the path to your signed Sparse Package (.msix). Note that these values cannot be relative paths and must be complete paths. +9. Build the app +10. Copy the PhotoStoreDemoPkg\Assets folder and resources.pri file to the same location as your VS Build binaries +11. Run the app + +### Removing the package +If you need to remove the package from package manager you can run the following command in an admin command prompt: + +powershell -c “get-appxpackage -name \*PackageWithExternalLocationCppSample\* | remove-appxpackage" \ No newline at end of file From c4b6bef300310f2a6185efb959f44059289d3fdf Mon Sep 17 00:00:00 2001 From: Ravish Roshan Date: Thu, 6 Nov 2025 07:55:12 +0530 Subject: [PATCH 10/20] Updated readme --- Samples/SampleChatAppWithShare/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Samples/SampleChatAppWithShare/README.md b/Samples/SampleChatAppWithShare/README.md index 15680b1..6911c02 100644 --- a/Samples/SampleChatAppWithShare/README.md +++ b/Samples/SampleChatAppWithShare/README.md @@ -28,9 +28,9 @@ A non-package native Windows desktop Win32 GUI application written in C++. It in 5. Install your cert on the machine 6. Create a Sparse Package by packaging the updated contents of PackageWithExternalLocationCppSample using [App Packager](https://docs.microsoft.com/en-us/windows/win32/appxpkg/make-appx-package--makeappx-exe-) (MakeAppx.exe) and specifying the **/nv** flag. For example: MakeAppx.exe pack /d \ /p \ mypackage.msix /nv 7. Sign the new Sparse Package. See [Signing an app package using SignTool](https://docs.microsoft.com/en-us/windows/win32/appxpkg/how-to-sign-a-package-using-signtool) or you can also use [Device Guard Signing](https://docs.microsoft.com/en-us/microsoft-store/device-guard-signing-portal). -8. In the WinMain method (in WinMain.cpp) update the value of **externalLocation** to match the output location of your VS Build binaries and the value of **packagePath** to match the path to your signed Sparse Package (.msix). Note that these values cannot be relative paths and must be complete paths. +8. In RegisterPackageWithExternalLocation() method (in PackageIdentity.cpp) update the value of **externalLocation** to match the output location of your VS Build binaries and the value of **packagePath** to match the path to your signed Sparse Package (.msix). Note that these values cannot be relative paths and must be complete paths. 9. Build the app -10. Copy the PhotoStoreDemoPkg\Assets folder and resources.pri file to the same location as your VS Build binaries +10. Copy the PhotoStoreDemoPkg\Assets folder and resources.pri file to the same location as your VS Build binaries. You can replace Assets with your own images. 11. Run the app ### Removing the package From 7702f8a345fe7e00c0e6769757d311a93f7bdda0 Mon Sep 17 00:00:00 2001 From: Ravish Roshan Date: Thu, 6 Nov 2025 08:01:07 +0530 Subject: [PATCH 11/20] Updated readme --- Samples/SampleChatAppWithShare/README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Samples/SampleChatAppWithShare/README.md b/Samples/SampleChatAppWithShare/README.md index 6911c02..c378030 100644 --- a/Samples/SampleChatAppWithShare/README.md +++ b/Samples/SampleChatAppWithShare/README.md @@ -10,11 +10,13 @@ You can learn more about Signed Sparse Packages and Identity, Registration & Act 2. Windows OS version 10.0.19000.0 + 3. Microsoft Visual C++ Redistributables -### PackageWithExternalLocationCppApp +### SampleChatAppWithShare A non-package native Windows desktop Win32 GUI application written in C++. It installs a Signed Sparse Package and uses it to act as a Share Target. -* Registration of a Signed Sparse Package and handling of Shared photos happens in WinMain.cpp. +* Registration of a Signed Sparse Package happens in PackageIdentity.cpp. + +* Handling of Shared Data happens in ShareTargetManager.cpp. * Files to package and sign to create a Sparse Package for use with the app are located in the PackageWithExternalLocationCppSample directory inside PackageWithExternalLocation sample. From f4ed112ea6a7814110739935a5be5288a4f8231e Mon Sep 17 00:00:00 2001 From: Ravish Roshan Date: Fri, 21 Nov 2025 04:41:42 +0530 Subject: [PATCH 12/20] This is the main app which will be communicating with the share app --- Samples/ChatAppShare/README.md | 41 ++ .../ChatAppShare/SampleChatAppWithShare.sln | 37 ++ .../SampleChatAppWithShare/ChatManager.cpp | 132 +++++ .../SampleChatAppWithShare/ChatManager.h | 10 + .../SampleChatAppWithShare/ChatModels.cpp | 48 ++ .../SampleChatAppWithShare/ChatModels.h | 32 ++ .../ContactSelectionDialog.cpp | 275 +++++++++++ .../ContactSelectionDialog.h | 45 ++ .../SampleChatAppWithShare/FileManager.cpp | 154 ++++++ .../SampleChatAppWithShare/FileManager.h | 13 + .../SampleChatAppWithShare/ModernUI.cpp | 128 +++++ .../SampleChatAppWithShare/ModernUI.h | 21 + .../PackageIdentity.cpp | 463 ++++++++++++++++++ .../SampleChatAppWithShare/PackageIdentity.h | 23 + .../SampleChatAppWithShare/Resource.h | 48 ++ .../SampleChatAppWithShare.cpp | 457 +++++++++++++++++ .../SampleChatAppWithShare.exe.manifest | 9 + .../SampleChatAppWithShare.h | 3 + .../SampleChatAppWithShare.ico | Bin 0 -> 46227 bytes .../SampleChatAppWithShare.rc | Bin 0 -> 9184 bytes .../SampleChatAppWithShare.vcxproj | 234 +++++++++ .../SampleChatAppWithShare.vcxproj.filters | 97 ++++ .../ShareTargetManager.cpp | 425 ++++++++++++++++ .../ShareTargetManager.h | 33 ++ .../SampleChatAppWithShare/UIConstants.h | 23 + .../SampleChatAppWithShare/UIManager.cpp | 142 ++++++ .../SampleChatAppWithShare/UIManager.h | 17 + .../SampleChatAppWithShare/WindowProcs.cpp | 168 +++++++ .../SampleChatAppWithShare/WindowProcs.h | 14 + .../SampleChatAppWithShare/framework.h | 29 ++ .../SampleChatAppWithShare/small.ico | Bin 0 -> 46227 bytes .../SampleChatAppWithShare/targetver.h | 6 + 32 files changed, 3127 insertions(+) create mode 100644 Samples/ChatAppShare/README.md create mode 100644 Samples/ChatAppShare/SampleChatAppWithShare.sln create mode 100644 Samples/ChatAppShare/SampleChatAppWithShare/ChatManager.cpp create mode 100644 Samples/ChatAppShare/SampleChatAppWithShare/ChatManager.h create mode 100644 Samples/ChatAppShare/SampleChatAppWithShare/ChatModels.cpp create mode 100644 Samples/ChatAppShare/SampleChatAppWithShare/ChatModels.h create mode 100644 Samples/ChatAppShare/SampleChatAppWithShare/ContactSelectionDialog.cpp create mode 100644 Samples/ChatAppShare/SampleChatAppWithShare/ContactSelectionDialog.h create mode 100644 Samples/ChatAppShare/SampleChatAppWithShare/FileManager.cpp create mode 100644 Samples/ChatAppShare/SampleChatAppWithShare/FileManager.h create mode 100644 Samples/ChatAppShare/SampleChatAppWithShare/ModernUI.cpp create mode 100644 Samples/ChatAppShare/SampleChatAppWithShare/ModernUI.h create mode 100644 Samples/ChatAppShare/SampleChatAppWithShare/PackageIdentity.cpp create mode 100644 Samples/ChatAppShare/SampleChatAppWithShare/PackageIdentity.h create mode 100644 Samples/ChatAppShare/SampleChatAppWithShare/Resource.h create mode 100644 Samples/ChatAppShare/SampleChatAppWithShare/SampleChatAppWithShare.cpp create mode 100644 Samples/ChatAppShare/SampleChatAppWithShare/SampleChatAppWithShare.exe.manifest create mode 100644 Samples/ChatAppShare/SampleChatAppWithShare/SampleChatAppWithShare.h create mode 100644 Samples/ChatAppShare/SampleChatAppWithShare/SampleChatAppWithShare.ico create mode 100644 Samples/ChatAppShare/SampleChatAppWithShare/SampleChatAppWithShare.rc create mode 100644 Samples/ChatAppShare/SampleChatAppWithShare/SampleChatAppWithShare.vcxproj create mode 100644 Samples/ChatAppShare/SampleChatAppWithShare/SampleChatAppWithShare.vcxproj.filters create mode 100644 Samples/ChatAppShare/SampleChatAppWithShare/ShareTargetManager.cpp create mode 100644 Samples/ChatAppShare/SampleChatAppWithShare/ShareTargetManager.h create mode 100644 Samples/ChatAppShare/SampleChatAppWithShare/UIConstants.h create mode 100644 Samples/ChatAppShare/SampleChatAppWithShare/UIManager.cpp create mode 100644 Samples/ChatAppShare/SampleChatAppWithShare/UIManager.h create mode 100644 Samples/ChatAppShare/SampleChatAppWithShare/WindowProcs.cpp create mode 100644 Samples/ChatAppShare/SampleChatAppWithShare/WindowProcs.h create mode 100644 Samples/ChatAppShare/SampleChatAppWithShare/framework.h create mode 100644 Samples/ChatAppShare/SampleChatAppWithShare/small.ico create mode 100644 Samples/ChatAppShare/SampleChatAppWithShare/targetver.h diff --git a/Samples/ChatAppShare/README.md b/Samples/ChatAppShare/README.md new file mode 100644 index 0000000..c378030 --- /dev/null +++ b/Samples/ChatAppShare/README.md @@ -0,0 +1,41 @@ +This repo contains a sample C++ application demonstrating how to use [Signed Sparse Packages](https://aka.ms/sparsepkgblog) to give a non-packaged desktop app access to new Windows APIs and features. + +## Instructions + +You can learn more about Signed Sparse Packages and Identity, Registration & Activation of Win32 apps in this [blogpost](https://aka.ms/sparsepkgblog) and in the [documentation](https://aka.ms/sparsepkgdocs). + +### Requirements + +1. Windows SDK version 10.0.19000.0 + +2. Windows OS version 10.0.19000.0 + +3. Microsoft Visual C++ Redistributables + +### SampleChatAppWithShare + +A non-package native Windows desktop Win32 GUI application written in C++. It installs a Signed Sparse Package and uses it to act as a Share Target. + +* Registration of a Signed Sparse Package happens in PackageIdentity.cpp. + +* Handling of Shared Data happens in ShareTargetManager.cpp. + +* Files to package and sign to create a Sparse Package for use with the app are located in the PackageWithExternalLocationCppSample directory inside PackageWithExternalLocation sample. + + +### Building and running the sample + +1. Make sure your machine has Developer Mode turned on. +2. Retarget the solution to the SDK version on your machine – Right click -> Retarget solution. +3. Add a project reference to the Windows.winmd file at "C:\Program Files (x86)\Windows Kits\10\UnionMetadata\\\Windows.winmd". (Right click PhotoStoreDemo project | Add | Reference| Browse | All files | Windows.winmd) +4. Update the Publisher value in the AppxManifest.xml file and in SampleChatAppWithShare.exe.manifest to match the Publisher value in your cert. If you need to create a cert for signing have a look at [Creating an app package signing certificate](https://docs.microsoft.com/en-us/windows/win32/appxpkg/how-to-create-a-package-signing-certificate). +5. Install your cert on the machine +6. Create a Sparse Package by packaging the updated contents of PackageWithExternalLocationCppSample using [App Packager](https://docs.microsoft.com/en-us/windows/win32/appxpkg/make-appx-package--makeappx-exe-) (MakeAppx.exe) and specifying the **/nv** flag. For example: MakeAppx.exe pack /d \ /p \ mypackage.msix /nv +7. Sign the new Sparse Package. See [Signing an app package using SignTool](https://docs.microsoft.com/en-us/windows/win32/appxpkg/how-to-sign-a-package-using-signtool) or you can also use [Device Guard Signing](https://docs.microsoft.com/en-us/microsoft-store/device-guard-signing-portal). +8. In RegisterPackageWithExternalLocation() method (in PackageIdentity.cpp) update the value of **externalLocation** to match the output location of your VS Build binaries and the value of **packagePath** to match the path to your signed Sparse Package (.msix). Note that these values cannot be relative paths and must be complete paths. +9. Build the app +10. Copy the PhotoStoreDemoPkg\Assets folder and resources.pri file to the same location as your VS Build binaries. You can replace Assets with your own images. +11. Run the app + +### Removing the package +If you need to remove the package from package manager you can run the following command in an admin command prompt: + +powershell -c “get-appxpackage -name \*PackageWithExternalLocationCppSample\* | remove-appxpackage" \ No newline at end of file diff --git a/Samples/ChatAppShare/SampleChatAppWithShare.sln b/Samples/ChatAppShare/SampleChatAppWithShare.sln new file mode 100644 index 0000000..8833b0a --- /dev/null +++ b/Samples/ChatAppShare/SampleChatAppWithShare.sln @@ -0,0 +1,37 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.14.36401.2 d17.14 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SampleChatAppWithShare", "SampleChatAppWithShare\SampleChatAppWithShare.vcxproj", "{DD6EC845-790A-4BD4-B638-AF0964704337}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|ARM64 = Debug|ARM64 + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|ARM64 = Release|ARM64 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {DD6EC845-790A-4BD4-B638-AF0964704337}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {DD6EC845-790A-4BD4-B638-AF0964704337}.Debug|ARM64.Build.0 = Debug|ARM64 + {DD6EC845-790A-4BD4-B638-AF0964704337}.Debug|x64.ActiveCfg = Debug|x64 + {DD6EC845-790A-4BD4-B638-AF0964704337}.Debug|x64.Build.0 = Debug|x64 + {DD6EC845-790A-4BD4-B638-AF0964704337}.Debug|x86.ActiveCfg = Debug|Win32 + {DD6EC845-790A-4BD4-B638-AF0964704337}.Debug|x86.Build.0 = Debug|Win32 + {DD6EC845-790A-4BD4-B638-AF0964704337}.Release|ARM64.ActiveCfg = Release|ARM64 + {DD6EC845-790A-4BD4-B638-AF0964704337}.Release|ARM64.Build.0 = Release|ARM64 + {DD6EC845-790A-4BD4-B638-AF0964704337}.Release|x64.ActiveCfg = Release|x64 + {DD6EC845-790A-4BD4-B638-AF0964704337}.Release|x64.Build.0 = Release|x64 + {DD6EC845-790A-4BD4-B638-AF0964704337}.Release|x86.ActiveCfg = Release|Win32 + {DD6EC845-790A-4BD4-B638-AF0964704337}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {6709D164-3EDF-4B1A-894A-2C561B2062CC} + EndGlobalSection +EndGlobal diff --git a/Samples/ChatAppShare/SampleChatAppWithShare/ChatManager.cpp b/Samples/ChatAppShare/SampleChatAppWithShare/ChatManager.cpp new file mode 100644 index 0000000..b00ebb6 --- /dev/null +++ b/Samples/ChatAppShare/SampleChatAppWithShare/ChatManager.cpp @@ -0,0 +1,132 @@ +#include "ChatManager.h" +#include "ChatModels.h" +#include "FileManager.h" +#include "UIManager.h" +#include +#include +#include + +void LoadContactChat(int contactIndex) +{ + if (!IsValidContactIndex(contactIndex)) return; + + selectedContactIndex = contactIndex; + const Contact& contact = contacts[contactIndex]; + + // Update contact name with status indicator + std::wstring headerText = contact.name + L" " + L" (" + contact.status + L")"; + SetWindowText(hContactName, headerText.c_str()); + + // Clear and populate chat display with better formatting + SetWindowText(hChatDisplay, L""); + + std::wstring chatText; + for (const auto& message : contact.messages) { + // Add timestamps and better message formatting + SYSTEMTIME st; + GetLocalTime(&st); + WCHAR timeStr[50]; + swprintf_s(timeStr, 50, L"[%02d:%02d] ", st.wHour, st.wMinute); + + if (message.find(L"You:") == 0) { + chatText += L" "; // Right align for your messages + chatText += timeStr + message + L"\r\n\r\n"; + } else { + chatText += timeStr + message + L"\r\n\r\n"; + } + } + + SetWindowText(hChatDisplay, chatText.c_str()); + + // Scroll to bottom + ::SendMessage(hChatDisplay, EM_SETSEL, -1, -1); + ::SendMessage(hChatDisplay, EM_SCROLLCARET, 0, 0); + + // Update shared files list + UpdateSharedFilesList(); + + // Refresh contact list to show selection + InvalidateRect(hContactsList, NULL, TRUE); +} + +void AddMessageToChat(const std::wstring& message, bool isOutgoing) +{ + Contact* contact = GetSelectedContact(); + if (!contact) return; + + // Add timestamp to message + SYSTEMTIME st; + GetLocalTime(&st); + WCHAR timeStr[50]; + swprintf_s(timeStr, 50, L"[%02d:%02d] ", st.wHour, st.wMinute); + + std::wstring formattedMessage; + if (isOutgoing) { + formattedMessage = L"You: " + message + L" ??"; + } else { + formattedMessage = contact->name + L": " + message; + } + + contact->messages.push_back(formattedMessage); + contact->lastMessage = message.length() > 50 ? message.substr(0, 47) + L"..." : message; + + // Update chat display + LoadContactChat(selectedContactIndex); + + // Refresh contacts list to update last message preview + InvalidateRect(hContactsList, NULL, TRUE); +} + +void SendChatMessage() +{ + if (selectedContactIndex < 0) return; + + WCHAR buffer[1024]; + GetWindowText(hMessageInput, buffer, 1024); + + std::wstring message(buffer); + if (!message.empty()) { + AddMessageToChat(message, true); + SetWindowText(hMessageInput, L""); + + // Simulate auto-reply after a short delay + SetTimer(GetParent(hMessageInput), 1, 2000, NULL); + } +} + +void ProcessAutoReply(HWND hWnd, int timerType) +{ + if (selectedContactIndex < 0) return; + + KillTimer(hWnd, timerType); + + if (timerType == 1) { + // Auto-reply from the selected contact with more variety + std::vector autoReplies = { + L"Got it!", + L"Thanks for letting me know!", + L"Sounds good!", + L"I'll get back to you soon.", + L"Perfect!", + L"Absolutely!", + L"Let me think about it.", + L"Great idea!" + }; + + int replyIndex = rand() % autoReplies.size(); + AddMessageToChat(autoReplies[replyIndex], false); + } + else if (timerType == 2) { + // Auto-reply acknowledging the shared file + std::vector fileReplies = { + L"Thanks for sharing the file!", + L"Got the file, will check it out.", + L"File received, thanks! ??", + L"Perfect timing, I needed this file.", + L"Awesome, downloading now!" + }; + + int replyIndex = rand() % fileReplies.size(); + AddMessageToChat(fileReplies[replyIndex], false); + } +} \ No newline at end of file diff --git a/Samples/ChatAppShare/SampleChatAppWithShare/ChatManager.h b/Samples/ChatAppShare/SampleChatAppWithShare/ChatManager.h new file mode 100644 index 0000000..daefb88 --- /dev/null +++ b/Samples/ChatAppShare/SampleChatAppWithShare/ChatManager.h @@ -0,0 +1,10 @@ +#pragma once + +#include +#include + +// Chat management functions +void LoadContactChat(int contactIndex); +void SendChatMessage(); +void AddMessageToChat(const std::wstring& message, bool isOutgoing); +void ProcessAutoReply(HWND hWnd, int timerType); \ No newline at end of file diff --git a/Samples/ChatAppShare/SampleChatAppWithShare/ChatModels.cpp b/Samples/ChatAppShare/SampleChatAppWithShare/ChatModels.cpp new file mode 100644 index 0000000..db4f520 --- /dev/null +++ b/Samples/ChatAppShare/SampleChatAppWithShare/ChatModels.cpp @@ -0,0 +1,48 @@ +#include "ChatModels.h" + +// Global data definitions +std::vector contacts; +std::map> chatHistory; +int selectedContactIndex = -1; + +void InitializeContacts() +{ + contacts = { + {L"Alice Johnson", L"Hey, how are you?", {L"Alice: Hey, how are you?", L"You: I'm doing great, thanks!", L"Alice: That's wonderful to hear!"}, {}, L"Available", true}, + {L"Bob Smith", L"See you tomorrow!", {L"Bob: Are we still meeting tomorrow?", L"You: Yes, see you at 3 PM", L"Bob: See you tomorrow!"}, {}, L"In a meeting", true}, + {L"Carol Williams", L"Thanks for the help", {L"Carol: Could you help me with the project?", L"You: Of course! What do you need?", L"Carol: Thanks for the help"}, {}, L"Available", true}, + {L"David Brown", L"Great presentation!", {L"David: Great presentation today!", L"You: Thank you! I'm glad you liked it"}, {}, L"Away", false}, + {L"Emma Davis", L"Coffee later?", {L"Emma: Want to grab coffee later?", L"You: Sure! What time works for you?", L"Emma: Coffee later?"}, {}, L"Available", true}, + {L"Frank Miller", L"Happy Birthday!", {L"Frank: Happy Birthday!", L"You: Thank you so much!"}, {}, L"Busy", true}, + {L"Grace Wilson", L"Meeting rescheduled", {L"Grace: Meeting has been rescheduled to 4 PM", L"You: Got it, thanks for letting me know"}, {}, L"Available", true}, + {L"Henry Taylor", L"Weekend plans?", {L"Henry: Any plans for the weekend?", L"You: Nothing concrete yet", L"Henry: Weekend plans?"}, {}, L"Offline", false}, + {L"Ivy Anderson", L"Project update", {L"Ivy: Here's the project update you requested", L"You: Perfect, reviewing it now"}, {}, L"Available", true}, + {L"Jack Thompson", L"Game night Friday", {L"Jack: Game night this Friday?", L"You: Count me in!", L"Jack: Game night Friday"}, {}, L"Gaming", true}, + {L"Kate Garcia", L"Recipe sharing", {L"Kate: Loved that recipe you shared!", L"You: I'm so glad you enjoyed it!"}, {}, L"Cooking", true}, + {L"Leo Martinez", L"Workout buddy", {L"Leo: Gym session tomorrow morning?", L"You: Absolutely! 7 AM as usual?"}, {}, L"At the gym", true}, + {L"Mia Rodriguez", L"Book recommendation", {L"Mia: Any good book recommendations?", L"You: I just finished a great mystery novel"}, {}, L"Reading", true}, + {L"Noah Lee", L"Tech discussion", {L"Noah: Thoughts on the new framework?", L"You: It looks promising! Want to discuss over lunch?"}, {}, L"Coding", true}, + {L"Olivia Clark", L"Travel planning", {L"Olivia: Planning the vacation itinerary", L"You: Excited to see what you've planned!"}, {}, L"Traveling", false} + }; + + // Add some sample shared files to demonstrate the feature + SYSTEMTIME st; + GetSystemTime(&st); + + contacts[0].sharedFiles.push_back({L"Project_Proposal.docx", L"C:\\Documents\\Project_Proposal.docx", L"Alice", st}); + contacts[1].sharedFiles.push_back({L"Meeting_Notes.pdf", L"C:\\Documents\\Meeting_Notes.pdf", L"Bob", st}); + contacts[2].sharedFiles.push_back({L"Budget_Spreadsheet.xlsx", L"C:\\Documents\\Budget_Spreadsheet.xlsx", L"Carol", st}); +} + +Contact* GetSelectedContact() +{ + if (IsValidContactIndex(selectedContactIndex)) { + return &contacts[selectedContactIndex]; + } + return nullptr; +} + +bool IsValidContactIndex(int index) +{ + return index >= 0 && index < (int)contacts.size(); +} \ No newline at end of file diff --git a/Samples/ChatAppShare/SampleChatAppWithShare/ChatModels.h b/Samples/ChatAppShare/SampleChatAppWithShare/ChatModels.h new file mode 100644 index 0000000..3fb7531 --- /dev/null +++ b/Samples/ChatAppShare/SampleChatAppWithShare/ChatModels.h @@ -0,0 +1,32 @@ +#pragma once + +#include +#include +#include +#include + +struct SharedFile { + std::wstring fileName; + std::wstring filePath; + std::wstring sharedBy; + SYSTEMTIME timeShared; +}; + +struct Contact { + std::wstring name; + std::wstring lastMessage; + std::vector messages; + std::vector sharedFiles; + std::wstring status; + bool isOnline; +}; + +// Global data +extern std::vector contacts; +extern std::map> chatHistory; +extern int selectedContactIndex; + +// Contact management functions +void InitializeContacts(); +Contact* GetSelectedContact(); +bool IsValidContactIndex(int index); \ No newline at end of file diff --git a/Samples/ChatAppShare/SampleChatAppWithShare/ContactSelectionDialog.cpp b/Samples/ChatAppShare/SampleChatAppWithShare/ContactSelectionDialog.cpp new file mode 100644 index 0000000..ba1aeca --- /dev/null +++ b/Samples/ChatAppShare/SampleChatAppWithShare/ContactSelectionDialog.cpp @@ -0,0 +1,275 @@ +#include "ContactSelectionDialog.h" +#include "Resource.h" +#include "UIConstants.h" +#include + +// Static member initialization +ContactSelectionDialog::SelectionResult ContactSelectionDialog::s_dialogResult; +std::wstring ContactSelectionDialog::s_currentFilePath; +std::wstring ContactSelectionDialog::s_currentFileName; + +ContactSelectionDialog::SelectionResult ContactSelectionDialog::ShowContactSelectionDialog(HWND hParent, const std::wstring& filePath, const std::wstring& fileName) +{ + // Store the file information for the dialog + s_currentFilePath = filePath; + s_currentFileName = fileName; + + // Reset the result + s_dialogResult = SelectionResult(); + + // Show the modal dialog + INT_PTR result = DialogBox(GetModuleHandle(NULL), MAKEINTRESOURCE(IDD_CONTACT_SELECTION), hParent, ContactSelectionDlgProc); + + if (result == IDOK) + { + s_dialogResult.wasSelected = true; + s_dialogResult.filePath = s_currentFilePath; + s_dialogResult.fileName = s_currentFileName; + } + else + { + s_dialogResult.wasSelected = false; + } + + return s_dialogResult; +} + +INT_PTR CALLBACK ContactSelectionDialog::ContactSelectionDlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) +{ + switch (message) + { + case WM_INITDIALOG: + { + // Set the dialog title with file name + std::wstring title = L"Share \"" + s_currentFileName + L"\" - Select Contact"; + SetWindowText(hDlg, title.c_str()); + + // Set up the dialog layout and styling + SetupDialogSizing(hDlg); + + // Initialize and populate the contact list + HWND hListBox = GetDlgItem(hDlg, IDC_CONTACT_SELECTION_LIST); + if (hListBox) + { + InitializeContactList(hListBox); + PopulateContactList(hListBox); + } + + // Set up the share message edit control + HWND hMessageEdit = GetDlgItem(hDlg, IDC_SHARE_MESSAGE_EDIT); + if (hMessageEdit) + { + std::wstring defaultMessage = L"I'm sharing \"" + s_currentFileName + L"\" with you!"; + SetWindowText(hMessageEdit, defaultMessage.c_str()); + } + + // Initially disable the Select button until a contact is chosen + EnableWindow(GetDlgItem(hDlg, IDC_SELECT_CONTACT_BUTTON), FALSE); + + // Center the dialog on the parent + RECT rcParent, rcDlg; + HWND hParent = GetParent(hDlg); + if (hParent) + { + GetWindowRect(hParent, &rcParent); + GetWindowRect(hDlg, &rcDlg); + + int x = rcParent.left + (rcParent.right - rcParent.left - (rcDlg.right - rcDlg.left)) / 2; + int y = rcParent.top + (rcParent.bottom - rcParent.top - (rcDlg.bottom - rcDlg.top)) / 2; + + SetWindowPos(hDlg, HWND_TOP, x, y, 0, 0, SWP_NOSIZE); + } + + return TRUE; + } + + case WM_COMMAND: + switch (LOWORD(wParam)) + { + case IDC_CONTACT_SELECTION_LIST: + if (HIWORD(wParam) == LBN_SELCHANGE) + { + int selectedIndex = (int)SendDlgItemMessage(hDlg, IDC_CONTACT_SELECTION_LIST, LB_GETCURSEL, 0, 0); + HandleContactSelection(hDlg, selectedIndex); + } + else if (HIWORD(wParam) == LBN_DBLCLK) + { + // Double-click selects and closes dialog + OnSelectContact(hDlg); + } + break; + + case IDC_SELECT_CONTACT_BUTTON: + OnSelectContact(hDlg); + break; + + case IDC_CANCEL_SELECTION_BUTTON: + case IDCANCEL: + OnCancel(hDlg); + break; + + case IDOK: + OnSelectContact(hDlg); + break; + } + break; + + case WM_CLOSE: + OnCancel(hDlg); + break; + } + + return FALSE; +} + +void ContactSelectionDialog::InitializeContactList(HWND hListBox) +{ + // Set up the list box for contact display + SendMessage(hListBox, LB_RESETCONTENT, 0, 0); +} + +void ContactSelectionDialog::PopulateContactList(HWND hListBox) +{ + // Clear existing items + SendMessage(hListBox, LB_RESETCONTENT, 0, 0); + + // Ensure contacts are initialized + if (contacts.empty()) + { + InitializeContacts(); + } + + // Debug logging + OutputDebugStringW((L"ContactSelectionDialog: Populating list with " + std::to_wstring(contacts.size()) + L" contacts\n").c_str()); + + // Add all contacts to the list + for (size_t i = 0; i < contacts.size(); ++i) + { + const Contact& contact = contacts[i]; + + // Create display text with contact name and status + std::wstring displayText = contact.name + L" - " + contact.status; + if (!contact.isOnline) + { + displayText += L" (Offline)"; + } + + int itemIndex = (int)SendMessage(hListBox, LB_ADDSTRING, 0, (LPARAM)displayText.c_str()); + + // Store the contact index as item data + SendMessage(hListBox, LB_SETITEMDATA, itemIndex, (LPARAM)i); + + // Debug log each contact being added + OutputDebugStringW((L"ContactSelectionDialog: Added contact " + std::to_wstring(i) + L": " + contact.name + L"\n").c_str()); + } + + // If no contacts were added, add a placeholder + if (contacts.empty()) + { + SendMessage(hListBox, LB_ADDSTRING, 0, (LPARAM)L"No contacts available"); + OutputDebugStringW(L"ContactSelectionDialog: No contacts available - added placeholder\n"); + } + else + { + OutputDebugStringW((L"ContactSelectionDialog: Successfully populated " + std::to_wstring(contacts.size()) + L" contacts\n").c_str()); + } +} + +void ContactSelectionDialog::HandleContactSelection(HWND hDlg, int selectedIndex) +{ + if (selectedIndex != LB_ERR) + { + // Enable the Select button when a contact is selected + EnableWindow(GetDlgItem(hDlg, IDC_SELECT_CONTACT_BUTTON), TRUE); + + // Get the contact index from item data + HWND hListBox = GetDlgItem(hDlg, IDC_CONTACT_SELECTION_LIST); + int contactIndex = (int)SendMessage(hListBox, LB_GETITEMDATA, selectedIndex, 0); + + if (contactIndex >= 0 && contactIndex < (int)contacts.size()) + { + // Update the share message with the selected contact's name + const Contact& contact = contacts[contactIndex]; + std::wstring personalizedMessage = L"Hey " + contact.name + L"! I'm sharing \"" + s_currentFileName + L"\" with you."; + + HWND hMessageEdit = GetDlgItem(hDlg, IDC_SHARE_MESSAGE_EDIT); + if (hMessageEdit) + { + SetWindowText(hMessageEdit, personalizedMessage.c_str()); + } + } + } + else + { + // Disable the Select button when no contact is selected + EnableWindow(GetDlgItem(hDlg, IDC_SELECT_CONTACT_BUTTON), FALSE); + } +} + +void ContactSelectionDialog::OnSelectContact(HWND hDlg) +{ + HWND hListBox = GetDlgItem(hDlg, IDC_CONTACT_SELECTION_LIST); + int selectedIndex = (int)SendMessage(hListBox, LB_GETCURSEL, 0, 0); + + if (selectedIndex == LB_ERR) + { + MessageBox(hDlg, L"Please select a contact to share with.", L"No Contact Selected", MB_OK | MB_ICONWARNING); + return; + } + + // Check if contacts are available + if (contacts.empty()) + { + MessageBox(hDlg, L"No contacts are available. Please ensure the application is properly initialized.", L"No Contacts Available", MB_OK | MB_ICONWARNING); + return; + } + + // Get the contact index from item data + int contactIndex = (int)SendMessage(hListBox, LB_GETITEMDATA, selectedIndex, 0); + + // Debug logging + OutputDebugStringW((L"ContactSelectionDialog: Selected contact index: " + std::to_wstring(contactIndex) + L", total contacts: " + std::to_wstring(contacts.size()) + L"\n").c_str()); + + if (contactIndex >= 0 && contactIndex < (int)contacts.size()) + { + // Get the share message + HWND hMessageEdit = GetDlgItem(hDlg, IDC_SHARE_MESSAGE_EDIT); + WCHAR messageBuffer[512] = {0}; + if (hMessageEdit) + { + GetWindowText(hMessageEdit, messageBuffer, 512); + } + + // Store the result + s_dialogResult.contactIndex = contactIndex; + s_dialogResult.shareMessage = messageBuffer; + + // Debug logging + OutputDebugStringW((L"ContactSelectionDialog: Contact selected - " + contacts[contactIndex].name + L"\n").c_str()); + + // Close dialog with success + EndDialog(hDlg, IDOK); + } + else + { + MessageBox(hDlg, L"Invalid contact selection. Please try again.", L"Selection Error", MB_OK | MB_ICONERROR); + OutputDebugStringW((L"ContactSelectionDialog: Invalid contact index: " + std::to_wstring(contactIndex) + L"\n").c_str()); + } +} + +void ContactSelectionDialog::OnCancel(HWND hDlg) +{ + // Close dialog with cancel + EndDialog(hDlg, IDCANCEL); +} + +void ContactSelectionDialog::SetupDialogSizing(HWND hDlg) +{ + // Set dialog size (approximately 400x500 pixels) + SetWindowPos(hDlg, NULL, 0, 0, 420, 520, SWP_NOMOVE | SWP_NOZORDER); +} + +void ContactSelectionDialog::ApplyModernStyling(HWND hDlg) +{ + // Modern styling is optional for now - skip to avoid dependencies +} \ No newline at end of file diff --git a/Samples/ChatAppShare/SampleChatAppWithShare/ContactSelectionDialog.h b/Samples/ChatAppShare/SampleChatAppWithShare/ContactSelectionDialog.h new file mode 100644 index 0000000..fc06ffe --- /dev/null +++ b/Samples/ChatAppShare/SampleChatAppWithShare/ContactSelectionDialog.h @@ -0,0 +1,45 @@ +#pragma once + +#include +#include +#include +#include "ChatModels.h" + +// Contact Selection Dialog Manager +class ContactSelectionDialog +{ +public: + // Structure to hold the result of contact selection + struct SelectionResult + { + bool wasSelected; + int contactIndex; + std::wstring shareMessage; + std::wstring filePath; + std::wstring fileName; + + SelectionResult() : wasSelected(false), contactIndex(-1) {} + }; + + // Show the contact selection dialog + static SelectionResult ShowContactSelectionDialog(HWND hParent, const std::wstring& filePath, const std::wstring& fileName); + +private: + // Dialog procedure for contact selection + static INT_PTR CALLBACK ContactSelectionDlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam); + + // Helper functions + static void InitializeContactList(HWND hListBox); + static void PopulateContactList(HWND hListBox); + static void UpdateContactListDisplay(HWND hListBox); + static void HandleContactSelection(HWND hDlg, int selectedIndex); + static void OnSelectContact(HWND hDlg); + static void OnCancel(HWND hDlg); + static void SetupDialogSizing(HWND hDlg); + static void ApplyModernStyling(HWND hDlg); + + // Static data for dialog communication + static SelectionResult s_dialogResult; + static std::wstring s_currentFilePath; + static std::wstring s_currentFileName; +}; \ No newline at end of file diff --git a/Samples/ChatAppShare/SampleChatAppWithShare/FileManager.cpp b/Samples/ChatAppShare/SampleChatAppWithShare/FileManager.cpp new file mode 100644 index 0000000..31ace5f --- /dev/null +++ b/Samples/ChatAppShare/SampleChatAppWithShare/FileManager.cpp @@ -0,0 +1,154 @@ +#include "FileManager.h" +#include "ChatModels.h" +#include "ChatManager.h" +#include "UIManager.h" +#include +#include + +#pragma comment(lib, "comdlg32.lib") + +// External declarations for UI window handles +extern HWND hSharedFilesList; +extern HWND hContactsList; + +void ShareFile() +{ + // Check if a contact is selected first + if (selectedContactIndex < 0) { + MessageBox(NULL, L"Please select a contact to share files with.", L"No Contact Selected", MB_OK | MB_ICONWARNING); + return; + } + + // Get the main window handle (parent of the share file button) + HWND hMainWindow = GetParent(hSharedFilesList); + while (GetParent(hMainWindow)) + { + hMainWindow = GetParent(hMainWindow); + } + + OPENFILENAME ofn; + WCHAR szFile[260] = {0}; + + ZeroMemory(&ofn, sizeof(ofn)); + ofn.lStructSize = sizeof(ofn); + ofn.hwndOwner = hMainWindow; // Set the main window as owner + ofn.lpstrFile = szFile; + ofn.nMaxFile = sizeof(szFile); + ofn.lpstrFilter = L"All Files\0*.*\0?? Text Files\0*.TXT\0?? Document Files\0*.DOC;*.DOCX\0??? Image Files\0*.BMP;*.JPG;*.PNG;*.GIF\0?? PDF Files\0*.PDF\0?? Excel Files\0*.XLS;*.XLSX\0"; + ofn.nFilterIndex = 1; + ofn.lpstrFileTitle = NULL; + ofn.nMaxFileTitle = 0; + ofn.lpstrInitialDir = NULL; + ofn.lpstrTitle = L"Select File to Share"; + ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_EXPLORER; + + if (GetOpenFileName(&ofn)) + { + // Extract file name from full path + std::wstring fullPath(szFile); + size_t lastSlash = fullPath.find_last_of(L"\\"); + std::wstring fileName; + if (lastSlash != std::wstring::npos) + { + fileName = fullPath.substr(lastSlash + 1); + } + else + { + fileName = fullPath; + } + + // Create shared file entry + SharedFile newFile; + newFile.fileName = fileName; + newFile.filePath = fullPath; + newFile.sharedBy = L"You"; + GetSystemTime(&newFile.timeShared); + + // Add file to the currently selected contact's shared files + if (selectedContactIndex >= 0 && selectedContactIndex < (int)contacts.size()) + { + contacts[selectedContactIndex].sharedFiles.push_back(newFile); + + // Add file sharing notification to chat + std::wstring fileShareMsg = L"?? Shared file: " + fileName; + AddMessageToChat(fileShareMsg, true); + + // Update UI + AddSharedFileToChat(newFile, true); + + // Update contacts list display to show the shared activity + InvalidateRect(hContactsList, NULL, TRUE); + + // Show success message with current contact + std::wstring successMsg = L"File \"" + fileName + L"\" has been shared with " + contacts[selectedContactIndex].name + L"!"; + MessageBox(hMainWindow, successMsg.c_str(), L"File Shared Successfully", MB_OK | MB_ICONINFORMATION); + + // Simulate auto-reply from contact acknowledging the file + SetTimer(hMainWindow, 2, 2000, NULL); + } + } +} + +void AddSharedFileToChat(const SharedFile& file, bool isOutgoing) +{ + Contact* contact = GetSelectedContact(); + if (!contact) return; + + std::wstring sharer = isOutgoing ? L"You" : contact->name; + + // Format file sharing message with timestamp + SYSTEMTIME st; + GetLocalTime(&st); + WCHAR timeStr[50]; + swprintf_s(timeStr, 50, L"[%02d:%02d] ", st.wHour, st.wMinute); + + std::wstring shareMessage = sharer + L" shared: " + file.fileName + L" ??"; + contact->messages.push_back(shareMessage); + + // Update last message preview + contact->lastMessage = L"?? " + file.fileName; + + // Add to shared files list + contact->sharedFiles.push_back(file); + + // Refresh UI + LoadContactChat(selectedContactIndex); + UpdateSharedFilesList(); +} + +void OpenSharedFile(int fileIndex) +{ + Contact* contact = GetSelectedContact(); + if (!contact || fileIndex < 0 || fileIndex >= (int)contact->sharedFiles.size()) { + return; + } + + const SharedFile& file = contact->sharedFiles[fileIndex]; + + // Try to open the file with the default application + HINSTANCE result = ShellExecute(NULL, L"open", file.filePath.c_str(), NULL, NULL, SW_SHOWNORMAL); + + if ((intptr_t)result <= 32) { + // If opening failed, show file location in explorer + std::wstring explorerCmd = L"/select,\"" + file.filePath + L"\""; + ShellExecute(NULL, L"open", L"explorer.exe", explorerCmd.c_str(), NULL, SW_SHOWNORMAL); + } +} + +void UpdateSharedFilesList() +{ + if (!hSharedFilesList) return; + + Contact* contact = GetSelectedContact(); + + // Clear the list + SendMessage(hSharedFilesList, LB_RESETCONTENT, 0, 0); + + if (!contact) return; + + // Add shared files to the list + for (const auto& file : contact->sharedFiles) { + std::wstring displayText = file.fileName + L" (shared by " + file.sharedBy + L")"; + SendMessage(hSharedFilesList, LB_ADDSTRING, 0, (LPARAM)displayText.c_str()); + } +} \ No newline at end of file diff --git a/Samples/ChatAppShare/SampleChatAppWithShare/FileManager.h b/Samples/ChatAppShare/SampleChatAppWithShare/FileManager.h new file mode 100644 index 0000000..e28e4db --- /dev/null +++ b/Samples/ChatAppShare/SampleChatAppWithShare/FileManager.h @@ -0,0 +1,13 @@ +#pragma once + +#include +#include +#include "ChatModels.h" + +// File management functions +void ShareFile(); +void AddSharedFileToChat(const SharedFile& file, bool isOutgoing); +void UpdateSharedFilesList(); +void OpenSharedFile(int fileIndex); +std::wstring GetFileExtensionIcon(const std::wstring& filePath); +std::wstring FormatFileSize(DWORD fileSize); \ No newline at end of file diff --git a/Samples/ChatAppShare/SampleChatAppWithShare/ModernUI.cpp b/Samples/ChatAppShare/SampleChatAppWithShare/ModernUI.cpp new file mode 100644 index 0000000..ca30eb2 --- /dev/null +++ b/Samples/ChatAppShare/SampleChatAppWithShare/ModernUI.cpp @@ -0,0 +1,128 @@ +#include "ModernUI.h" +#include "UIConstants.h" +#include + +#pragma comment(lib, "gdiplus.lib") + +// Modern UI Variables definitions +HBRUSH hBrushBackground = nullptr; +HBRUSH hBrushSurface = nullptr; +HBRUSH hBrushPrimary = nullptr; +HBRUSH hBrushHover = nullptr; +HFONT hFontRegular = nullptr; +HFONT hFontBold = nullptr; +HFONT hFontTitle = nullptr; +HPEN hPenBorder = nullptr; + +void InitializeModernUI() +{ + // Create brushes for modern color scheme + hBrushBackground = CreateSolidBrush(COLOR_APP_BACKGROUND); + hBrushSurface = CreateSolidBrush(COLOR_SURFACE); + hBrushPrimary = CreateSolidBrush(COLOR_PRIMARY); + hBrushHover = CreateSolidBrush(COLOR_HOVER); + + // Create modern fonts + hFontRegular = CreateFont(16, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, + DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, + CLEARTYPE_QUALITY, DEFAULT_PITCH | FF_DONTCARE, L"Segoe UI"); + + hFontBold = CreateFont(16, 0, 0, 0, FW_SEMIBOLD, FALSE, FALSE, FALSE, + DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, + CLEARTYPE_QUALITY, DEFAULT_PITCH | FF_DONTCARE, L"Segoe UI"); + + hFontTitle = CreateFont(20, 0, 0, 0, FW_SEMIBOLD, FALSE, FALSE, FALSE, + DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, + CLEARTYPE_QUALITY, DEFAULT_PITCH | FF_DONTCARE, L"Segoe UI"); + + // Create pen for borders + hPenBorder = CreatePen(PS_SOLID, 1, COLOR_BORDER); + + // Initialize GDI+ + Gdiplus::GdiplusStartupInput gdiplusStartupInput; + ULONG_PTR gdiplusToken; + Gdiplus::GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL); +} + +void CleanupModernUI() +{ + // Cleanup GDI objects + if (hBrushBackground) DeleteObject(hBrushBackground); + if (hBrushSurface) DeleteObject(hBrushSurface); + if (hBrushPrimary) DeleteObject(hBrushPrimary); + if (hBrushHover) DeleteObject(hBrushHover); + if (hFontRegular) DeleteObject(hFontRegular); + if (hFontBold) DeleteObject(hFontBold); + if (hFontTitle) DeleteObject(hFontTitle); + if (hPenBorder) DeleteObject(hPenBorder); + + // Shutdown GDI+ + Gdiplus::GdiplusShutdown(NULL); +} + +void DrawModernButton(HDC hdc, RECT rect, const std::wstring& text, bool isHovered, bool isPressed) +{ + // Create rounded rectangle region + HRGN hRgn = CreateRoundRectRgn(rect.left, rect.top, rect.right, rect.bottom, 8, 8); + + // Fill background + HBRUSH hBrush = CreateSolidBrush(isPressed ? COLOR_PRIMARY_DARK : + isHovered ? COLOR_PRIMARY : COLOR_PRIMARY); + FillRgn(hdc, hRgn, hBrush); + DeleteObject(hBrush); + + // Draw border with a brush instead of pen + HBRUSH borderBrush = CreateSolidBrush(COLOR_BORDER); + FrameRgn(hdc, hRgn, borderBrush, 1, 1); + DeleteObject(borderBrush); + DeleteObject(hRgn); + + // Draw text + SetBkMode(hdc, TRANSPARENT); + SetTextColor(hdc, RGB(255, 255, 255)); + SelectObject(hdc, hFontBold); + + DrawText(hdc, text.c_str(), -1, &rect, DT_CENTER | DT_VCENTER | DT_SINGLELINE); +} + +void DrawContactItem(HDC hdc, RECT rect, const Contact& contact, bool isSelected) +{ + // Fill background + HBRUSH bgBrush = CreateSolidBrush(isSelected ? COLOR_HOVER : COLOR_SURFACE); + FillRect(hdc, &rect, bgBrush); + DeleteObject(bgBrush); + + // Draw avatar circle + int avatarX = rect.left + 12; + int avatarY = rect.top + (rect.bottom - rect.top - AVATAR_SIZE) / 2; + + HBRUSH avatarBrush = CreateSolidBrush(COLOR_PRIMARY); + HPEN avatarPen = CreatePen(PS_SOLID, 2, contact.isOnline ? RGB(34, 197, 94) : RGB(156, 163, 175)); + + SelectObject(hdc, avatarBrush); + SelectObject(hdc, avatarPen); + + Ellipse(hdc, avatarX, avatarY, avatarX + AVATAR_SIZE, avatarY + AVATAR_SIZE); + + DeleteObject(avatarBrush); + DeleteObject(avatarPen); + + // Draw contact name + SetBkMode(hdc, TRANSPARENT); + SetTextColor(hdc, COLOR_TEXT_PRIMARY); + SelectObject(hdc, hFontBold); + + RECT nameRect = {avatarX + AVATAR_SIZE + 12, rect.top + 8, rect.right - 8, rect.top + 28}; + DrawText(hdc, contact.name.c_str(), -1, &nameRect, DT_LEFT | DT_VCENTER | DT_SINGLELINE); + + // Draw status + SetTextColor(hdc, COLOR_TEXT_SECONDARY); + SelectObject(hdc, hFontRegular); + + RECT statusRect = {avatarX + AVATAR_SIZE + 12, rect.top + 30, rect.right - 8, rect.top + 48}; + DrawText(hdc, contact.status.c_str(), -1, &statusRect, DT_LEFT | DT_VCENTER | DT_SINGLELINE); + + // Draw last message preview + RECT msgRect = {avatarX + AVATAR_SIZE + 12, rect.top + 50, rect.right - 8, rect.bottom - 8}; + DrawText(hdc, contact.lastMessage.c_str(), -1, &msgRect, DT_LEFT | DT_VCENTER | DT_SINGLELINE | DT_END_ELLIPSIS); +} \ No newline at end of file diff --git a/Samples/ChatAppShare/SampleChatAppWithShare/ModernUI.h b/Samples/ChatAppShare/SampleChatAppWithShare/ModernUI.h new file mode 100644 index 0000000..3cb9ea7 --- /dev/null +++ b/Samples/ChatAppShare/SampleChatAppWithShare/ModernUI.h @@ -0,0 +1,21 @@ +#pragma once + +#include +#include +#include "ChatModels.h" + +// Modern UI Variables +extern HBRUSH hBrushBackground; +extern HBRUSH hBrushSurface; +extern HBRUSH hBrushPrimary; +extern HBRUSH hBrushHover; +extern HFONT hFontRegular; +extern HFONT hFontBold; +extern HFONT hFontTitle; +extern HPEN hPenBorder; + +// Modern UI Functions +void InitializeModernUI(); +void CleanupModernUI(); +void DrawModernButton(HDC hdc, RECT rect, const std::wstring& text, bool isHovered, bool isPressed); +void DrawContactItem(HDC hdc, RECT rect, const Contact& contact, bool isSelected); \ No newline at end of file diff --git a/Samples/ChatAppShare/SampleChatAppWithShare/PackageIdentity.cpp b/Samples/ChatAppShare/SampleChatAppWithShare/PackageIdentity.cpp new file mode 100644 index 0000000..4f4ace9 --- /dev/null +++ b/Samples/ChatAppShare/SampleChatAppWithShare/PackageIdentity.cpp @@ -0,0 +1,463 @@ +#include "framework.h" +#include "PackageIdentity.h" +#include +#include +#include +#include +#include + +// Package Identity Variables +bool g_isSparsePackageSupported = false; +bool g_isRunningWithIdentity = false; +bool g_packageIdentityInitialized = false; + +// Initialize package identity management +bool InitializePackageIdentity() +{ + if (g_packageIdentityInitialized) + return true; + + OutputDebugStringW(L"ChatApp: Initializing package identity management...\n"); + + // Check OS support for sparse packages + g_isSparsePackageSupported = IsSparsePackageSupported(); + + // Check if already running with identity + g_isRunningWithIdentity = IsRunningWithIdentity(); + + g_packageIdentityInitialized = true; + + wchar_t statusLog[256]; + swprintf_s(statusLog, L"ChatApp: Package Identity Status - Sparse supported: %s, Has identity: %s\n", + g_isSparsePackageSupported ? L"Yes" : L"No", + g_isRunningWithIdentity ? L"Yes" : L"No"); + OutputDebugStringW(statusLog); + + return true; +} + +// Register package with external location (improved implementation) +HRESULT RegisterPackageWithExternalLocation(const std::wstring& externalLocation, const std::wstring& packagePath) +{ + try + { + OutputDebugStringW(L"ChatApp: Attempting to register package with external location...\n"); + + wchar_t logBuffer[512]; + swprintf_s(logBuffer, L"ChatApp: External location: %s\n", externalLocation.c_str()); + OutputDebugStringW(logBuffer); + swprintf_s(logBuffer, L"ChatApp: Package path: %s\n", packagePath.c_str()); + OutputDebugStringW(logBuffer); + + // Check if the package file exists + DWORD fileAttributes = GetFileAttributesW(packagePath.c_str()); + if (fileAttributes == INVALID_FILE_ATTRIBUTES) + { + OutputDebugStringW(L"ChatApp: Package file not found, trying PowerShell registration method.\n"); + return RegisterPackageWithExternalLocationPowerShell(externalLocation, packagePath); + } + + // Try PowerShell registration first as it's more reliable + HRESULT powershellResult = RegisterPackageWithExternalLocationPowerShell(externalLocation, packagePath); + if (SUCCEEDED(powershellResult)) + { + OutputDebugStringW(L"ChatApp: Package registration via PowerShell succeeded.\n"); + return powershellResult; + } + + // If PowerShell failed, log the error + wchar_t errorLog[256]; + swprintf_s(errorLog, L"ChatApp: PowerShell registration failed with HRESULT: 0x%08X\n", powershellResult); + OutputDebugStringW(errorLog); + + // For now, return the PowerShell result since we don't have other registration methods implemented + return powershellResult; + } + catch (...) + { + OutputDebugStringW(L"ChatApp: Exception occurred during package registration\n"); + return E_FAIL; + } +} + +// Alternative implementation using PowerShell for package registration +HRESULT RegisterPackageWithExternalLocationPowerShell(const std::wstring& externalLocation, const std::wstring& packagePath) +{ + try + { + OutputDebugStringW(L"ChatApp: Attempting PowerShell package registration...\n"); + + // Check if the package file exists + DWORD fileAttributes = GetFileAttributesW(packagePath.c_str()); + if (fileAttributes == INVALID_FILE_ATTRIBUTES) + { + wchar_t errorLog[512]; + swprintf_s(errorLog, L"ChatApp: Package file not found at: %s\n", packagePath.c_str()); + OutputDebugStringW(errorLog); + return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); + } + + // Build PowerShell command for MSIX package registration with external location + // Use Add-AppxPackage with -ExternalLocation parameter + std::wstring powershellCmd = L"powershell.exe -ExecutionPolicy Bypass -Command \""; + powershellCmd += L"try { "; + powershellCmd += L"Add-AppxPackage -Path '"; + powershellCmd += packagePath; + powershellCmd += L"' -ExternalLocation '"; + powershellCmd += externalLocation; + powershellCmd += L"' -ForceTargetApplicationShutdown; "; + powershellCmd += L"Write-Host 'Package registration successful'; "; + powershellCmd += L"exit 0; "; + powershellCmd += L"} catch { "; + powershellCmd += L"Write-Host ('Package registration failed: ' + $_.Exception.Message); "; + powershellCmd += L"exit 1; "; + powershellCmd += L"}\""; + + wchar_t logBuffer[1024]; + swprintf_s(logBuffer, L"ChatApp: PowerShell command: %s\n", powershellCmd.c_str()); + OutputDebugStringW(logBuffer); + + // Execute PowerShell command + STARTUPINFOW si = {}; + PROCESS_INFORMATION pi = {}; + si.cb = sizeof(si); + si.dwFlags = STARTF_USESHOWWINDOW; + si.wShowWindow = SW_HIDE; + + BOOL success = CreateProcessW( + nullptr, + const_cast(powershellCmd.c_str()), + nullptr, + nullptr, + FALSE, + CREATE_NO_WINDOW, + nullptr, + nullptr, + &si, + &pi + ); + + if (success) + { + OutputDebugStringW(L"ChatApp: PowerShell process started, waiting for completion...\n"); + + // Wait for PowerShell to complete (with timeout) + DWORD waitResult = WaitForSingleObject(pi.hProcess, 60000); // Increased timeout for package registration + + DWORD exitCode = 0; + if (waitResult == WAIT_OBJECT_0) + { + GetExitCodeProcess(pi.hProcess, &exitCode); + wchar_t exitLog[256]; + swprintf_s(exitLog, L"ChatApp: PowerShell process completed with exit code: %d\n", exitCode); + OutputDebugStringW(exitLog); + } + else if (waitResult == WAIT_TIMEOUT) + { + OutputDebugStringW(L"ChatApp: PowerShell process timed out. Package registration may still be in progress.\n"); + TerminateProcess(pi.hProcess, 1); + exitCode = 1; + } + else + { + DWORD waitError = GetLastError(); + wchar_t waitLog[256]; + swprintf_s(waitLog, L"ChatApp: Wait failed with error: %d\n", waitError); + OutputDebugStringW(waitLog); + exitCode = 1; + } + + CloseHandle(pi.hProcess); + CloseHandle(pi.hThread); + + return (exitCode == 0) ? S_OK : E_FAIL; + } + else + { + DWORD error = GetLastError(); + wchar_t errorLog[256]; + swprintf_s(errorLog, L"ChatApp: Failed to start PowerShell process. Error: %d\n", error); + OutputDebugStringW(errorLog); + return HRESULT_FROM_WIN32(error); + } + } + catch (...) + { + OutputDebugStringW(L"ChatApp: Exception occurred during PowerShell package registration\n"); + return E_FAIL; + } +} + +// Relaunch the current application +void RelaunchApplication() +{ + OutputDebugStringW(L"ChatApp: Attempting to relaunch application...\n"); + + wchar_t exePath[MAX_PATH] = {0}; + DWORD len = GetModuleFileNameW(nullptr, exePath, MAX_PATH); + if (len == 0 || len == MAX_PATH) + { + OutputDebugStringW(L"ChatApp: Failed to get executable path for relaunch.\n"); + return; + } + + // Log the executable path + wchar_t logBuffer[512]; + swprintf_s(logBuffer, L"ChatApp: Relaunching: %s\n", exePath); + OutputDebugStringW(logBuffer); + + // Add a small delay to allow package registration to complete + Sleep(2000); + + // Use ShellExecuteW to relaunch the current executable + HINSTANCE result = ShellExecuteW(nullptr, L"open", exePath, nullptr, nullptr, SW_SHOWNORMAL); + + // Fix: Use proper casting for x64 compatibility + INT_PTR resultValue = reinterpret_cast(result); + if (resultValue <= 32) + { + // Log the error + wchar_t errorLog[256]; + swprintf_s(errorLog, L"ChatApp: Failed to relaunch application. ShellExecute error code: %lld\n", + static_cast(resultValue)); + OutputDebugStringW(errorLog); + + // Try alternative relaunch method using CreateProcess + OutputDebugStringW(L"ChatApp: Trying alternative relaunch method...\n"); + + STARTUPINFOW si = {}; + PROCESS_INFORMATION pi = {}; + si.cb = sizeof(si); + si.dwFlags = STARTF_USESHOWWINDOW; + si.wShowWindow = SW_SHOWNORMAL; + + BOOL createResult = CreateProcessW( + exePath, + nullptr, + nullptr, + nullptr, + FALSE, + 0, + nullptr, + nullptr, + &si, + &pi + ); + + if (createResult) + { + OutputDebugStringW(L"ChatApp: Alternative relaunch method succeeded.\n"); + CloseHandle(pi.hProcess); + CloseHandle(pi.hThread); + } + else + { + DWORD createError = GetLastError(); + wchar_t createErrorLog[256]; + swprintf_s(createErrorLog, L"ChatApp: Alternative relaunch also failed. Error: %d\n", createError); + OutputDebugStringW(createErrorLog); + } + } + else + { + OutputDebugStringW(L"ChatApp: Application relaunch initiated successfully.\n"); + } +} + +// Checks if the OS version is Windows 10 2004 (build 19041) or later +bool IsSparsePackageSupported() +{ + // Windows 10 2004 is version 10.0.19041 + OSVERSIONINFOEXW osvi = {}; + osvi.dwOSVersionInfoSize = sizeof(osvi); + + // Get the actual version using RtlGetVersion (undocumented but reliable) + typedef LONG (WINAPI* RtlGetVersionPtr)(PRTL_OSVERSIONINFOEXW); + HMODULE hMod = ::GetModuleHandleW(L"ntdll.dll"); + if (hMod) { + RtlGetVersionPtr fxPtr = (RtlGetVersionPtr)::GetProcAddress(hMod, "RtlGetVersion"); + if (fxPtr != nullptr) { + fxPtr((PRTL_OSVERSIONINFOEXW)&osvi); + + // Log version information for debugging + wchar_t log[256]; + swprintf_s(log, L"ChatApp: Current OS Version: %u.%u.%u\n", + osvi.dwMajorVersion, osvi.dwMinorVersion, osvi.dwBuildNumber); + OutputDebugStringW(log); + } + } + + // Compare with required version (Windows 10 2004 build 19041) + if (osvi.dwMajorVersion > 10 || + (osvi.dwMajorVersion == 10 && osvi.dwMinorVersion > 0) || + (osvi.dwMajorVersion == 10 && osvi.dwMinorVersion == 0 && osvi.dwBuildNumber >= 19041)) + { + OutputDebugStringW(L"ChatApp: Sparse package is supported on this OS.\n"); + return true; + } + + OutputDebugStringW(L"ChatApp: Sparse package is NOT supported on this OS.\n"); + return false; +} + +// Returns true if the app is running with package identity +bool IsRunningWithIdentity() +{ + UINT32 length = 0; + LONG rc = GetCurrentPackageFullName(&length, nullptr); + + if (rc == ERROR_INSUFFICIENT_BUFFER) + { + std::vector packageFullName(length); + rc = GetCurrentPackageFullName(&length, packageFullName.data()); + if (rc == ERROR_SUCCESS) + { + OutputDebugStringW(L"ChatApp: Running with package identity.\n"); + return true; + } + } + + OutputDebugStringW(L"ChatApp: Not running with package identity.\n"); + return false; +} + +// Helper to get the directory of the current executable +std::wstring GetExecutableDirectory() +{ + wchar_t exePath[MAX_PATH] = {0}; + DWORD len = GetModuleFileNameW(nullptr, exePath, MAX_PATH); + if (len == 0 || len == MAX_PATH) + { + OutputDebugStringW(L"ChatApp: Failed to get executable path.\n"); + return L""; + } + + std::wstring path(exePath); + size_t pos = path.find_last_of(L"\\/"); + if (pos != std::wstring::npos) + path = path.substr(0, pos); + + return path; +} + +// Initialize package identity-specific flows and features +void InitializePackageIdentityFlow() +{ + OutputDebugStringW(L"ChatApp: Initializing package identity-specific flows...\n"); + + if (!g_isRunningWithIdentity) { + OutputDebugStringW(L"ChatApp: Not running with package identity - skipping identity-specific flows.\n"); + return; + } + + // TODO: Add package identity-specific initialization here: + + // 1. Single Instance Management + // - Check for existing app instances + // - Register current instance + // - Handle instance redirection + + // 2. Share Target Registration + // - Check for share target activation + // - Initialize share target handlers + // - Set up cross-process communication + + // 3. Enhanced Security Features + // - Initialize secure file handling + // - Set up identity-based permissions + // - Enable enhanced data protection + + // 4. App Model Integration + // - Register activation handlers + // - Set up background task support + // - Initialize notification system + + OutputDebugStringW(L"ChatApp: Package identity flows initialized (placeholder - features to be implemented).\n"); +} + +// Helper to validate MSIX package existence and basic properties +bool ValidateMsixPackage(const std::wstring& packagePath) +{ + OutputDebugStringW(L"ChatApp: Validating MSIX package...\n"); + + // Check if file exists + DWORD fileAttributes = GetFileAttributesW(packagePath.c_str()); + if (fileAttributes == INVALID_FILE_ATTRIBUTES) + { + wchar_t errorLog[512]; + swprintf_s(errorLog, L"ChatApp: MSIX package not found at: %s\n", packagePath.c_str()); + OutputDebugStringW(errorLog); + return false; + } + + // Check if it's a file (not a directory) + if (fileAttributes & FILE_ATTRIBUTE_DIRECTORY) + { + OutputDebugStringW(L"ChatApp: Package path points to a directory, not a file.\n"); + return false; + } + + // Check file extension + size_t dotPos = packagePath.find_last_of(L'.'); + if (dotPos == std::wstring::npos) + { + OutputDebugStringW(L"ChatApp: Package file has no extension.\n"); + return false; + } + + std::wstring extension = packagePath.substr(dotPos); + std::transform(extension.begin(), extension.end(), extension.begin(), ::towlower); + + if (extension != L".msix" && extension != L".appx") + { + wchar_t extLog[256]; + swprintf_s(extLog, L"ChatApp: Package has unexpected extension: %s\n", extension.c_str()); + OutputDebugStringW(extLog); + return false; + } + + // Get file size + HANDLE hFile = CreateFileW(packagePath.c_str(), GENERIC_READ, FILE_SHARE_READ, + nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr); + if (hFile != INVALID_HANDLE_VALUE) + { + LARGE_INTEGER fileSize; + if (GetFileSizeEx(hFile, &fileSize)) + { + wchar_t sizeLog[256]; + swprintf_s(sizeLog, L"ChatApp: Package size: %lld bytes\n", fileSize.QuadPart); + OutputDebugStringW(sizeLog); + } + CloseHandle(hFile); + } + + OutputDebugStringW(L"ChatApp: MSIX package validation passed.\n"); + return true; +} + +// Get current package identity status as a formatted string +std::wstring GetPackageIdentityStatus() +{ + std::wstring status = L"ChatApp Package Identity Status:\n"; + status += L"- Sparse package supported: " + std::wstring(g_isSparsePackageSupported ? L"Yes" : L"No") + L"\n"; + status += L"- Running with identity: " + std::wstring(g_isRunningWithIdentity ? L"Yes" : L"No") + L"\n"; + status += L"- Initialized: " + std::wstring(g_packageIdentityInitialized ? L"Yes" : L"No") + L"\n"; + + if (g_isRunningWithIdentity) + { + // Try to get package full name + UINT32 length = 0; + LONG rc = GetCurrentPackageFullName(&length, nullptr); + if (rc == ERROR_INSUFFICIENT_BUFFER && length > 0) + { + std::vector packageFullName(length); + rc = GetCurrentPackageFullName(&length, packageFullName.data()); + if (rc == ERROR_SUCCESS) + { + status += L"- Package full name: " + std::wstring(packageFullName.data()) + L"\n"; + } + } + } + + return status; +} \ No newline at end of file diff --git a/Samples/ChatAppShare/SampleChatAppWithShare/PackageIdentity.h b/Samples/ChatAppShare/SampleChatAppWithShare/PackageIdentity.h new file mode 100644 index 0000000..e97fa85 --- /dev/null +++ b/Samples/ChatAppShare/SampleChatAppWithShare/PackageIdentity.h @@ -0,0 +1,23 @@ +#pragma once + +#include +#include + +// Package Identity Variables +extern bool g_isSparsePackageSupported; +extern bool g_isRunningWithIdentity; +extern bool g_packageIdentityInitialized; + +// Package Identity Management functions +bool IsSparsePackageSupported(); +bool IsRunningWithIdentity(); +bool InitializePackageIdentity(); +void InitializePackageIdentityFlow(); +std::wstring GetExecutableDirectory(); +HRESULT RegisterPackageWithExternalLocation(const std::wstring& externalLocation, const std::wstring& packagePath); +HRESULT RegisterPackageWithExternalLocationPowerShell(const std::wstring& externalLocation, const std::wstring& packagePath); +void RelaunchApplication(); + +// Helper functions +bool ValidateMsixPackage(const std::wstring& packagePath); +std::wstring GetPackageIdentityStatus(); \ No newline at end of file diff --git a/Samples/ChatAppShare/SampleChatAppWithShare/Resource.h b/Samples/ChatAppShare/SampleChatAppWithShare/Resource.h new file mode 100644 index 0000000..e41d7d3 --- /dev/null +++ b/Samples/ChatAppShare/SampleChatAppWithShare/Resource.h @@ -0,0 +1,48 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by SampleChatAppWithShare.rc + +#define IDS_APP_TITLE 103 + +#define IDR_MAINFRAME 128 +#define IDD_SAMPLECHATAPPWITHSHARE_DIALOG 102 +#define IDD_ABOUTBOX 103 +#define IDD_CONTACT_SELECTION 104 +#define IDM_ABOUT 105 +#define IDM_EXIT 106 +#define IDI_SAMPLECHATAPPWITHSHARE 107 +#define IDI_SMALL 108 +#define IDC_SAMPLECHATAPPWITHSHARE 109 +#define IDC_MYICON 2 + +// Chat Application Controls +#define IDC_CONTACTS_LIST 1001 +#define IDC_CHAT_DISPLAY 1002 +#define IDC_MESSAGE_INPUT 1003 +#define IDC_SEND_BUTTON 1004 +#define IDC_CONTACT_NAME 1005 +#define IDC_SHARE_FILE_BUTTON 1006 +#define IDC_SHARED_FILES_LIST 1007 + +// Contact Selection Dialog Controls +#define IDC_CONTACT_SELECTION_LIST 1008 +#define IDC_SELECT_CONTACT_BUTTON 1009 +#define IDC_CANCEL_SELECTION_BUTTON 1010 +#define IDC_SHARE_MESSAGE_EDIT 1011 +#define IDC_DIALOG_TITLE 1012 + +#ifndef IDC_STATIC +#define IDC_STATIC -1 +#endif +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS + +#define _APS_NO_MFC 130 +#define _APS_NEXT_RESOURCE_VALUE 129 +#define _APS_NEXT_COMMAND_VALUE 32771 +#define _APS_NEXT_CONTROL_VALUE 1013 +#define _APS_NEXT_SYMED_VALUE 110 +#endif +#endif diff --git a/Samples/ChatAppShare/SampleChatAppWithShare/SampleChatAppWithShare.cpp b/Samples/ChatAppShare/SampleChatAppWithShare/SampleChatAppWithShare.cpp new file mode 100644 index 0000000..ff6cdcd --- /dev/null +++ b/Samples/ChatAppShare/SampleChatAppWithShare/SampleChatAppWithShare.cpp @@ -0,0 +1,457 @@ +// SampleChatAppWithShare.cpp : Defines the entry point for the application. +// + +#include "framework.h" +#include "SampleChatAppWithShare.h" +#include "UIConstants.h" +#include "ChatModels.h" +#include "ModernUI.h" +#include "ChatManager.h" +#include "FileManager.h" +#include "UIManager.h" +#include "PackageIdentity.h" +#include "ShareTargetManager.h" +#include "WindowProcs.h" +#include +#include +#include +#include +#include + +#pragma comment(lib, "comctl32.lib") +#pragma comment(lib, "shell32.lib") +#pragma comment(lib, "uxtheme.lib") +#pragma comment(lib, "ole32.lib") +// Add WinRT libraries to fix linking error +#pragma comment(lib, "windowsapp.lib") +#pragma comment(lib, "runtimeobject.lib") + +using namespace winrt; +using namespace Windows::Foundation; +using namespace Windows::ApplicationModel; +using namespace Windows::ApplicationModel::Activation; +using namespace Windows::ApplicationModel::DataTransfer; +using namespace Windows::ApplicationModel::DataTransfer::ShareTarget; + +// Global Variables: +HINSTANCE hInst; // current instance +WCHAR szTitle[MAX_LOADSTRING]; // The title bar text +WCHAR szWindowClass[MAX_LOADSTRING]; // the main window class name + +// Global flag to track if we're in share-only mode +bool g_isShareOnlyMode = false; + +// Forward declarations of functions included in this code module: +ATOM MyRegisterClass(HINSTANCE hInstance); +BOOL InitInstance(HINSTANCE, int); +LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); +INT_PTR CALLBACK About(HWND, UINT, WPARAM, LPARAM); + +int APIENTRY wWinMain(_In_ HINSTANCE hInstance, + _In_opt_ HINSTANCE hPrevInstance, + _In_ LPWSTR lpCmdLine, + _In_ int nCmdShow) +{ + UNREFERENCED_PARAMETER(hPrevInstance); + UNREFERENCED_PARAMETER(lpCmdLine); + + // Init package identity checks + InitializePackageIdentity(); + + // Initialize WinRT with proper error handling + try + { + winrt::init_apartment(); + OutputDebugStringW(L"ChatApp: WinRT initialized successfully.\n"); + } + catch (hresult_error const& ex) + { + std::wstring errorMsg = L"ChatApp: WinRT initialization failed: " + std::wstring(ex.message().c_str()) + + L" (HRESULT: 0x" + std::to_wstring(static_cast(ex.code())) + L")\n"; + OutputDebugStringW(errorMsg.c_str()); + } + catch (...) + { + OutputDebugStringW(L"ChatApp: WinRT initialization failed with unknown error.\n"); + } + + // Initialize Share Target Manager + ShareTargetManager::Initialize(); + + // Note: Don't process share target activation here - UI isn't created yet + // This will be handled after UI creation in WndProc WM_CREATE + + // Check if this is a share target activation to determine if we need the main window + if (g_isRunningWithIdentity && ShareTargetManager::IsShareTargetActivation()) + { + g_isShareOnlyMode = true; + OutputDebugStringW(L"ChatApp: Running in share-only mode - main window will not be created\n"); + } + + if (g_isSparsePackageSupported && !g_isRunningWithIdentity) + { + std::wstring executableDir = GetExecutableDirectory(); + std::wstring packagePath = executableDir + L"\\Weixin_1.0.1.0_x86__v4k3sbdawh17a.msix"; + + // Validate the MSIX package before attempting registration + if (ValidateMsixPackage(packagePath)) + { + HRESULT result = RegisterPackageWithExternalLocation(executableDir, packagePath); + if (SUCCEEDED(result)) + { + OutputDebugStringW(L"ChatApp: Package registration succeeded. Relaunching...\n"); + RelaunchApplication(); + return 0; // Exit after relaunch + } + else + { + // Log the error but continue without package identity + wchar_t errorLog[256]; + swprintf_s(errorLog, L"ChatApp: Failed to register package. HRESULT: 0x%08X. Continuing without package identity.\n", result); + OutputDebugStringW(errorLog); + } + } + else + { + OutputDebugStringW(L"ChatApp: MSIX package validation failed. Continuing without package identity.\n"); + } + } + else if (g_isRunningWithIdentity) + { + // Process share target activation using the ShareTargetManager + ShareTargetManager::ProcessActivationArgs(); + } + else + { + // Log the current status + std::wstring status = GetPackageIdentityStatus(); + OutputDebugStringW(status.c_str()); + } + + // Initialize COM for shell operations + CoInitialize(NULL); + + // Initialize common controls + INITCOMMONCONTROLSEX icex; + icex.dwSize = sizeof(INITCOMMONCONTROLSEX); + icex.dwICC = ICC_LISTVIEW_CLASSES | ICC_STANDARD_CLASSES; + InitCommonControlsEx(&icex); + + // Initialize global strings + LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING); + LoadStringW(hInstance, IDC_SAMPLECHATAPPWITHSHARE, szWindowClass, MAX_LOADSTRING); + + if (!g_isShareOnlyMode) + { + // Only register window class if we're not in share-only mode + MyRegisterClass(hInstance); + + // Initialize modern UI + InitializeModernUI(); + } + + // Initialize dummy contacts (needed for share target) + InitializeContacts(); + + if (g_isShareOnlyMode) + { + // In share-only mode, process the share target directly without creating a window + OutputDebugStringW(L"ChatApp: Processing share target in share-only mode\n"); + ShareTargetManager::ProcessActivationArgs(); + } + else + { + // Normal mode: create and show the main window + if (!InitInstance(hInstance, nCmdShow)) + { + return FALSE; + } + } + + HACCEL hAccelTable = nullptr; + if (!g_isShareOnlyMode) + { + hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_SAMPLECHATAPPWITHSHARE)); + } + + MSG msg; + + // Main message loop: + while (GetMessage(&msg, nullptr, 0, 0)) + { + if (!g_isShareOnlyMode && !TranslateAccelerator(msg.hwnd, hAccelTable, &msg)) + { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + else if (g_isShareOnlyMode) + { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + } + + CoUninitialize(); + return (int) msg.wParam; +} + +// +// FUNCTION: MyRegisterClass() +// +// PURPOSE: Registers the window class. +// +ATOM MyRegisterClass(HINSTANCE hInstance) +{ + WNDCLASSEXW wcex; + + wcex.cbSize = sizeof(WNDCLASSEX); + + wcex.style = CS_HREDRAW | CS_VREDRAW; + wcex.lpfnWndProc = WndProc; + wcex.cbClsExtra = 0; + wcex.cbWndExtra = 0; + wcex.hInstance = hInstance; + wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_SAMPLECHATAPPWITHSHARE)); + wcex.hCursor = LoadCursor(nullptr, IDC_ARROW); + wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); + wcex.lpszMenuName = MAKEINTRESOURCEW(IDC_SAMPLECHATAPPWITHSHARE); + wcex.lpszClassName = szWindowClass; + wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL)); + + return RegisterClassExW(&wcex); +} + +// +// FUNCTION: InitInstance(HINSTANCE, int) +// +// PURPOSE: Saves instance handle and creates main window +// +// COMMENTS: +// +// In this function, we save the instance handle in a global variable and +// create and display the main program window. +// +BOOL InitInstance(HINSTANCE hInstance, int nCmdShow) +{ + hInst = hInstance; // Store instance handle in our global variable + + HWND hWnd = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW, + CW_USEDEFAULT, 0, 1200, 700, nullptr, nullptr, hInstance, nullptr); + + if (!hWnd) + { + return FALSE; + } + + ShowWindow(hWnd, nCmdShow); + UpdateWindow(hWnd); + + return TRUE; +} + +// +// FUNCTION: WndProc(HWND, UINT, WPARAM, LPARAM) +// +// PURPOSE: Processes messages for the main window. +// +LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + switch (message) + { + case WM_CREATE: + CreateChatUI(hWnd); + + // Process share target activation after UI is created + if (g_isRunningWithIdentity) + { + ShareTargetManager::ProcessActivationArgs(); + } + break; + + case WM_SIZE: + ResizeChatUI(hWnd); + break; + + case WM_CTLCOLORSTATIC: + { + HDC hdcStatic = (HDC)wParam; + SetTextColor(hdcStatic, COLOR_TEXT_PRIMARY); + SetBkColor(hdcStatic, COLOR_APP_BACKGROUND); + return (INT_PTR)hBrushBackground; + } + + case WM_CTLCOLOREDIT: + { + HDC hdcEdit = (HDC)wParam; + SetTextColor(hdcEdit, COLOR_TEXT_PRIMARY); + SetBkColor(hdcEdit, COLOR_SURFACE); + return (INT_PTR)hBrushSurface; + } + + case WM_CTLCOLORLISTBOX: + { + HDC hdcList = (HDC)wParam; + SetTextColor(hdcList, COLOR_TEXT_PRIMARY); + SetBkColor(hdcList, COLOR_SURFACE); + return (INT_PTR)hBrushSurface; + } + + case WM_PAINT: + { + PAINTSTRUCT ps; + HDC hdc = BeginPaint(hWnd, &ps); + + // Fill the main window background with modern color + RECT rect; + GetClientRect(hWnd, &rect); + FillRect(hdc, &rect, hBrushBackground); + + EndPaint(hWnd, &ps); + } + break; + + case WM_COMMAND: + { + int wmId = LOWORD(wParam); + int wmEvent = HIWORD(wParam); + + // Parse the menu selections: + switch (wmId) + { + case IDM_ABOUT: + DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About); + break; + case IDM_EXIT: + DestroyWindow(hWnd); + break; + case IDC_CONTACTS_LIST: + if (wmEvent == LBN_SELCHANGE) { + int selectedIndex = (int)::SendMessage(hContactsList, LB_GETCURSEL, 0, 0); + LoadContactChat(selectedIndex); + UpdateSharedFilesList(); + } + break; + case IDC_SEND_BUTTON: + SendChatMessage(); + break; + case IDC_SHARE_FILE_BUTTON: + ShareFile(); + break; + case IDC_SHARED_FILES_LIST: + if (wmEvent == LBN_DBLCLK) { + int selectedFile = (int)::SendMessage(hSharedFilesList, LB_GETCURSEL, 0, 0); + OpenSharedFile(selectedFile); + } + break; + case IDC_MESSAGE_INPUT: + if (wmEvent == EN_CHANGE) { + // Enable/disable send button based on input with visual feedback + WCHAR buffer[1024]; + GetWindowText(hMessageInput, buffer, 1024); + bool hasText = wcslen(buffer) > 0; + EnableWindow(hSendButton, hasText); + InvalidateRect(hSendButton, NULL, FALSE); + } + break; + case 2000: // Test WinRT Share Target Status + { + // Use ShareTargetManager to get status + std::wstring statusInfo = ShareTargetManager::GetShareTargetStatus(); + MessageBoxW(hWnd, statusInfo.c_str(), L"WinRT Share Target Status", MB_OK | MB_ICONINFORMATION); + } + break; + default: + return DefWindowProc(hWnd, message, wParam, lParam); + } + } + break; + + case WM_TIMER: + ProcessAutoReply(hWnd, (int)wParam); + break; + + case WM_KEYDOWN: + { + if (GetFocus() == hMessageInput && wParam == VK_RETURN) { + if (!(GetKeyState(VK_SHIFT) & 0x8000)) { + // Enter without Shift sends the message + SendChatMessage(); + return 0; + } + } + } + break; + + case WM_DESTROY: + CleanupModernUI(); + PostQuitMessage(0); + break; + + default: + return DefWindowProc(hWnd, message, wParam, lParam); + } + return 0; +} + +// Message handler for about box. +INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) +{ + UNREFERENCED_PARAMETER(lParam); + switch (message) + { + case WM_INITDIALOG: + { + // Add WinRT and package identity information to the about dialog + std::wstring aboutText = L"Chat Application with WinRT Share Target Support\n\n"; + + // Test WinRT status + try + { + winrt::check_hresult(S_OK); + aboutText += L"? WinRT Status: ? Initialized and Working\n"; + + if (g_isRunningWithIdentity) + { + if (ShareTargetManager::IsShareTargetActivation()) + { + aboutText += L"?? Share Target: ? Currently Activated via Windows Share Sheet\n"; + } + else + { + aboutText += L"?? Share Target: ? Ready for Activation\n"; + } + } + else + { + aboutText += L"?? Share Target: ? Package Identity Required\n"; + } + } + catch (...) + { + aboutText += L"?? WinRT Status: ? Error or Not Available\n"; + } + + aboutText += L"?? Package Identity: " + std::wstring(g_isRunningWithIdentity ? L"? Available" : L"? Not Available") + L"\n"; + aboutText += L"?? Sparse Package Support: " + std::wstring(g_isSparsePackageSupported ? L"? Supported" : L"? Not Supported") + L"\n\n"; + aboutText += L"Current Features:\n"; + aboutText += L"? WinRT Runtime Integration\n"; + aboutText += L"? Windows Share Target Support\n"; + aboutText += L"? Package Identity Management\n"; + aboutText += L"? MSIX Package Registration\n"; + aboutText += L"? Modern Chat UI\n\n"; + aboutText += GetPackageIdentityStatus(); + + SetDlgItemTextW(hDlg, IDC_STATIC, aboutText.c_str()); + return (INT_PTR)TRUE; + } + + case WM_COMMAND: + if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL) + { + EndDialog(hDlg, LOWORD(wParam)); + return (INT_PTR)TRUE; + } + break; + } + return (INT_PTR)FALSE; +} diff --git a/Samples/ChatAppShare/SampleChatAppWithShare/SampleChatAppWithShare.exe.manifest b/Samples/ChatAppShare/SampleChatAppWithShare/SampleChatAppWithShare.exe.manifest new file mode 100644 index 0000000..92ae86a --- /dev/null +++ b/Samples/ChatAppShare/SampleChatAppWithShare/SampleChatAppWithShare.exe.manifest @@ -0,0 +1,9 @@ + + + + + diff --git a/Samples/ChatAppShare/SampleChatAppWithShare/SampleChatAppWithShare.h b/Samples/ChatAppShare/SampleChatAppWithShare/SampleChatAppWithShare.h new file mode 100644 index 0000000..d00d47e --- /dev/null +++ b/Samples/ChatAppShare/SampleChatAppWithShare/SampleChatAppWithShare.h @@ -0,0 +1,3 @@ +#pragma once + +#include "resource.h" diff --git a/Samples/ChatAppShare/SampleChatAppWithShare/SampleChatAppWithShare.ico b/Samples/ChatAppShare/SampleChatAppWithShare/SampleChatAppWithShare.ico new file mode 100644 index 0000000000000000000000000000000000000000..b3ec03bd617f32e58128fa977fd6ac9605124f4b GIT binary patch literal 46227 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 f|Mq_nzRG%O*r_3Pp + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + Debug + ARM64 + + + Release + ARM64 + + + + 17.0 + Win32Proj + {dd6ec845-790a-4bd4-b638-af0964704337} + SampleChatAppWithShare + 10.0 + + + + Application + true + v143 + Unicode + + + Application + false + v143 + true + Unicode + + + Application + true + v143 + Unicode + + + Application + false + v143 + true + Unicode + + + Application + true + v143 + Unicode + + + Application + false + v143 + true + Unicode + + + + + + + + + + + + + + + + + + + + + + + + + + + + Level3 + true + WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions) + true + stdcpp17 + + + Windows + true + + + + + Level3 + true + true + true + WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions) + true + stdcpp17 + + + Windows + true + + + + + Level3 + true + _DEBUG;_WINDOWS;%(PreprocessorDefinitions) + true + stdcpp17 + + + Windows + true + + + + + Level3 + true + true + true + NDEBUG;_WINDOWS;%(PreprocessorDefinitions) + true + stdcpp17 + + + Windows + true + + + + + Level3 + true + _DEBUG;_WINDOWS;%(PreprocessorDefinitions) + true + stdcpp17 + + + Windows + true + + + + + Level3 + true + true + true + NDEBUG;_WINDOWS;%(PreprocessorDefinitions) + true + stdcpp17 + + + Windows + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Samples/ChatAppShare/SampleChatAppWithShare/SampleChatAppWithShare.vcxproj.filters b/Samples/ChatAppShare/SampleChatAppWithShare/SampleChatAppWithShare.vcxproj.filters new file mode 100644 index 0000000..1bc4c91 --- /dev/null +++ b/Samples/ChatAppShare/SampleChatAppWithShare/SampleChatAppWithShare.vcxproj.filters @@ -0,0 +1,97 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Resource Files + + + + + Resource Files + + + Resource Files + + + + + + \ No newline at end of file diff --git a/Samples/ChatAppShare/SampleChatAppWithShare/ShareTargetManager.cpp b/Samples/ChatAppShare/SampleChatAppWithShare/ShareTargetManager.cpp new file mode 100644 index 0000000..82131dc --- /dev/null +++ b/Samples/ChatAppShare/SampleChatAppWithShare/ShareTargetManager.cpp @@ -0,0 +1,425 @@ +#include "ShareTargetManager.h" +#include "PackageIdentity.h" +#include "ContactSelectionDialog.h" +#include "ChatManager.h" +#include "ChatModels.h" +#include "FileManager.h" +#include "framework.h" +#include + +// Static member initialization +bool ShareTargetManager::s_initialized = false; +bool ShareTargetManager::s_shareTargetSupported = false; + +void ShareTargetManager::Initialize() +{ + if (s_initialized) + return; + + LogShareInfo(L"Initializing Share Target Manager..."); + + try + { + // Check if WinRT and package identity are available + if (g_isRunningWithIdentity) + { + s_shareTargetSupported = true; + LogShareInfo(L"Share Target support available with package identity."); + } + else + { + s_shareTargetSupported = false; + LogShareInfo(L"Share Target requires package identity (not available)."); + } + } + catch (winrt::hresult_error const& ex) + { + std::wstring error = L"Share Target initialization error: " + std::wstring(ex.message().c_str()); + LogShareError(error); + s_shareTargetSupported = false; + } + catch (...) + { + LogShareError(L"Unknown error during Share Target initialization."); + s_shareTargetSupported = false; + } + + s_initialized = true; + LogShareInfo(L"Share Target Manager initialized successfully."); +} + +bool ShareTargetManager::IsShareTargetAvailable() +{ + if (!s_initialized) + Initialize(); + + return s_shareTargetSupported && g_isRunningWithIdentity; +} + +bool ShareTargetManager::IsShareTargetActivation() +{ + if (!IsShareTargetAvailable()) + return false; + + try + { + auto activationArgs = winrt::Windows::ApplicationModel::AppInstance::GetActivatedEventArgs(); + return activationArgs && activationArgs.Kind() == winrt::Windows::ApplicationModel::Activation::ActivationKind::ShareTarget; + } + catch (...) + { + LogShareError(L"Error checking share target activation."); + return false; + } +} + +bool ShareTargetManager::ProcessActivationArgs() +{ + if (!IsShareTargetAvailable()) + return false; + + // Add a static flag to prevent multiple processing + static bool s_alreadyProcessed = false; + if (s_alreadyProcessed) { + LogShareInfo(L"Share target already processed - skipping duplicate call"); + return false; + } + + try + { + auto activationArgs = winrt::Windows::ApplicationModel::AppInstance::GetActivatedEventArgs(); + if (activationArgs && activationArgs.Kind() == winrt::Windows::ApplicationModel::Activation::ActivationKind::ShareTarget) + { + LogShareInfo(L"? Share Target activation detected!"); + s_alreadyProcessed = true; // Mark as processed + + // Ensure contacts are initialized (critical for share target scenarios) + if (contacts.empty()) + { + LogShareInfo(L"Initializing contacts for share target scenario..."); + InitializeContacts(); + } + + // Process the share target directly here + auto shareArgs = activationArgs.as(); + auto shareOperation = shareArgs.ShareOperation(); + auto data = shareOperation.Data(); + + // Extract shared content information for the contact selection dialog + std::wstring sharedContentSummary = L"Shared Content"; + std::wstring sharedFiles; + bool hasFiles = false; + + // Collect information about what's being shared + std::vector sharedItems; + + // Check for different data formats + if (data.Contains(winrt::Windows::ApplicationModel::DataTransfer::StandardDataFormats::Text())) + { + try + { + auto textAsync = data.GetTextAsync(); + auto text = textAsync.get(); + sharedItems.push_back(L"Text: " + std::wstring(text.c_str())); + LogShareInfo(L"Received shared text."); + } + catch (...) + { + sharedItems.push_back(L"Text: [Error retrieving text]"); + LogShareError(L"Error retrieving shared text."); + } + } + + if (data.Contains(winrt::Windows::ApplicationModel::DataTransfer::StandardDataFormats::WebLink())) + { + try + { + auto webLinkAsync = data.GetWebLinkAsync(); + auto webLink = webLinkAsync.get(); + sharedItems.push_back(L"Web Link: " + std::wstring(webLink.ToString().c_str())); + LogShareInfo(L"Received shared web link."); + } + catch (...) + { + sharedItems.push_back(L"Web Link: [Error retrieving web link]"); + LogShareError(L"Error retrieving shared web link."); + } + } + + if (data.Contains(winrt::Windows::ApplicationModel::DataTransfer::StandardDataFormats::Bitmap())) + { + try + { + auto bitmapAsync = data.GetBitmapAsync(); + auto bitmapRef = bitmapAsync.get(); + sharedItems.push_back(L"Image/Bitmap content"); + LogShareInfo(L"Received shared bitmap."); + } + catch (...) + { + sharedItems.push_back(L"Image: [Error retrieving image]"); + LogShareError(L"Error retrieving shared bitmap."); + } + } + + if (data.Contains(winrt::Windows::ApplicationModel::DataTransfer::StandardDataFormats::StorageItems())) + { + try + { + auto storageItemsAsync = data.GetStorageItemsAsync(); + auto storageItems = storageItemsAsync.get(); + + hasFiles = true; + std::wstring filesInfo = std::to_wstring(storageItems.Size()) + L" file(s)"; + + if (storageItems.Size() == 1) + { + auto item = storageItems.GetAt(0); + sharedFiles = item.Name().c_str(); + sharedContentSummary = sharedFiles; + } + else if (storageItems.Size() > 1) + { + sharedFiles = L"Multiple Files (" + std::to_wstring(storageItems.Size()) + L")"; + sharedContentSummary = sharedFiles; + } + + for (uint32_t i = 0; i < storageItems.Size(); i++) + { + auto item = storageItems.GetAt(i); + filesInfo += L"\n - " + std::wstring(item.Name().c_str()); + } + + sharedItems.push_back(filesInfo); + LogShareInfo(L"Received shared files."); + } + catch (...) + { + sharedItems.push_back(L"Files: [Error retrieving files]"); + LogShareError(L"Error retrieving shared files."); + } + } + + // If no specific content type found, use generic description + if (sharedItems.empty()) + { + sharedContentSummary = L"Shared Content"; + sharedItems.push_back(L"Unknown content type"); + } + + // Get the main window handle for dialog parent + HWND hMainWindow = GetActiveWindow(); + if (!hMainWindow) + { + hMainWindow = GetForegroundWindow(); + } + if (!hMainWindow) + { + // Try to find the main chat application window + hMainWindow = FindWindow(NULL, L"Chat Application"); + } + + // Log the number of contacts available for debugging + LogShareInfo(L"Available contacts: " + std::to_wstring(contacts.size())); + + // Show contact selection dialog for the shared content + ContactSelectionDialog::SelectionResult result = + ContactSelectionDialog::ShowContactSelectionDialog(hMainWindow, L"", sharedContentSummary); + + if (result.wasSelected) + { + // User selected a contact - add the shared content to that contact's chat + if (result.contactIndex >= 0 && result.contactIndex < (int)contacts.size()) + { + int previousSelection = selectedContactIndex; + selectedContactIndex = result.contactIndex; + + LogShareInfo(L"Setting selectedContactIndex to: " + std::to_wstring(result.contactIndex)); + LogShareInfo(L"Selected contact name: " + contacts[result.contactIndex].name); + + // Add messages directly to the selected contact instead of relying on selectedContactIndex + Contact& selectedContact = contacts[result.contactIndex]; + + // Add the custom share message if provided + if (!result.shareMessage.empty()) + { + std::wstring formattedShareMessage = L"You: " + result.shareMessage + L" ??"; + selectedContact.messages.push_back(formattedShareMessage); + LogShareInfo(L"Added share message: " + result.shareMessage); + } + + // Add shared content messages to the chat + for (const auto& item : sharedItems) + { + std::wstring shareMsg = L"?? Received via Share: " + item; + std::wstring formattedMessage = selectedContact.name + L": " + shareMsg; + selectedContact.messages.push_back(formattedMessage); + LogShareInfo(L"Added shared content message: " + shareMsg); + } + + // Update the last message preview for this contact + if (!sharedItems.empty()) + { + std::wstring lastMsg = L"?? Received via Share: " + sharedItems[0]; + selectedContact.lastMessage = lastMsg.length() > 50 ? lastMsg.substr(0, 47) + L"..." : lastMsg; + } + + // If files were shared, add them to the contact's shared files list + if (hasFiles && data.Contains(winrt::Windows::ApplicationModel::DataTransfer::StandardDataFormats::StorageItems())) + { + try + { + auto storageItemsAsync = data.GetStorageItemsAsync(); + auto storageItems = storageItemsAsync.get(); + + for (uint32_t i = 0; i < storageItems.Size(); i++) + { + auto item = storageItems.GetAt(i); + + // Create shared file entry + SharedFile newFile; + newFile.fileName = item.Name().c_str(); + newFile.filePath = item.Path().c_str(); // Get full path if available + newFile.sharedBy = L"External Share"; + GetSystemTime(&newFile.timeShared); + + selectedContact.sharedFiles.push_back(newFile); + LogShareInfo(L"Added shared file: " + newFile.fileName); + } + } + catch (...) + { + LogShareError(L"Error adding shared files to contact."); + } + } + + // Show success message and exit the application + std::wstring successMsg = L"Content has been shared successfully with " + contacts[result.contactIndex].name + L"!\n\nThe application will now close."; + MessageBoxW(hMainWindow, successMsg.c_str(), L"Sharing Complete", MB_OK | MB_ICONINFORMATION); + + LogShareInfo(L"Share target content added to contact: " + contacts[result.contactIndex].name); + LogShareInfo(L"Selected contact index set to: " + std::to_wstring(result.contactIndex)); + LogShareInfo(L"Contact now has " + std::to_wstring(selectedContact.messages.size()) + L" messages"); + + // Report completion to Windows + shareOperation.ReportCompleted(); + + LogShareInfo(L"Share target processing completed successfully. Exiting application."); + + // Exit the application after successful sharing + PostQuitMessage(0); + return true; + } + } + else + { + // User cancelled - show a brief message and exit + MessageBoxW(hMainWindow, L"Share operation was cancelled.\n\nThe application will now close.", L"Share Cancelled", MB_OK | MB_ICONINFORMATION); + LogShareInfo(L"Share target operation cancelled by user."); + + // Report completion to Windows + shareOperation.ReportCompleted(); + + LogShareInfo(L"Share target processing cancelled. Exiting application."); + + // Exit the application after cancellation + PostQuitMessage(0); + return true; + } + + // Report completion to Windows + shareOperation.ReportCompleted(); + + LogShareInfo(L"Share target processing completed successfully."); + return true; + } + else + { + LogShareInfo(L"Running with package identity but not as share target."); + return false; + } + } + catch (winrt::hresult_error const& ex) + { + s_alreadyProcessed = true; // Mark as processed even on error to prevent retries + std::wstring error = L"Error checking activation args: " + std::wstring(ex.message().c_str()) + + L" (HRESULT: 0x" + std::to_wstring(static_cast(ex.code())) + L")"; + LogShareError(error); + MessageBoxW(nullptr, L"Error processing shared content", L"Share Target Error", MB_OK | MB_ICONERROR); + return false; + } + catch (...) + { + s_alreadyProcessed = true; // Mark as processed even on error to prevent retries + LogShareError(L"Unknown error checking activation arguments."); + MessageBoxW(nullptr, L"Unknown error processing shared content", L"Share Target Error", MB_OK | MB_ICONERROR); + return false; + } +} + +std::wstring ShareTargetManager::GetShareTargetStatus() +{ + if (!s_initialized) + Initialize(); + + std::wstring status = L"WinRT Share Target Integration Status:\n\n"; + + try + { + winrt::check_hresult(S_OK); + status += L"?? WinRT Runtime: ? Available and Working\n"; + status += L"?? Package Identity: " + std::wstring(g_isRunningWithIdentity ? L"? Available" : L"? Not Available") + L"\n"; + status += L"?? Sparse Package Support: " + std::wstring(g_isSparsePackageSupported ? L"? Supported" : L"? Not Supported") + L"\n\n"; + + // Test activation + if (g_isRunningWithIdentity) + { + try + { + if (IsShareTargetActivation()) + { + status += L"?? Share Target: ? Currently Activated\n"; + } + else + { + status += L"?? Share Target: ?? Not Currently Activated\n"; + } + } + catch (...) + { + status += L"?? Share Target: ? Error Checking Activation\n"; + } + } + else + { + status += L"?? Share Target: ? Package Identity Required\n"; + } + + status += GetPackageIdentityStatus(); + status += L"\n\n?? WinRT Share Target integration is complete and ready!"; + } + catch (winrt::hresult_error const& ex) + { + status += L"?? WinRT Runtime: ? Error\n"; + status += L"Error: " + std::wstring(ex.message().c_str()) + L"\n"; + status += L"HRESULT: 0x" + std::to_wstring(static_cast(ex.code())) + L"\n"; + } + catch (...) + { + status += L"?? WinRT Runtime: ? Unknown Error\n"; + } + + return status; +} + +void ShareTargetManager::LogShareInfo(const std::wstring& message) +{ + std::wstring logMessage = L"ChatApp: " + message + L"\n"; + OutputDebugStringW(logMessage.c_str()); +} + +void ShareTargetManager::LogShareError(const std::wstring& error) +{ + std::wstring logMessage = L"ChatApp: ShareTarget ERROR - " + error + L"\n"; + OutputDebugStringW(logMessage.c_str()); +} \ No newline at end of file diff --git a/Samples/ChatAppShare/SampleChatAppWithShare/ShareTargetManager.h b/Samples/ChatAppShare/SampleChatAppWithShare/ShareTargetManager.h new file mode 100644 index 0000000..2281410 --- /dev/null +++ b/Samples/ChatAppShare/SampleChatAppWithShare/ShareTargetManager.h @@ -0,0 +1,33 @@ +#pragma once + +#include +#include + +// Simple Share Target Manager with minimal dependencies +class ShareTargetManager +{ +public: + // Initialize the share target manager + static void Initialize(); + + // Check if the current activation is a share target + static bool IsShareTargetActivation(); + + // Check activation arguments and process if it's a share target + static bool ProcessActivationArgs(); + + // Get share target status information + static std::wstring GetShareTargetStatus(); + + // Check if share target is available (requires package identity) + static bool IsShareTargetAvailable(); + +private: + // Logging helpers + static void LogShareInfo(const std::wstring& message); + static void LogShareError(const std::wstring& error); + +private: + static bool s_initialized; + static bool s_shareTargetSupported; +}; \ No newline at end of file diff --git a/Samples/ChatAppShare/SampleChatAppWithShare/UIConstants.h b/Samples/ChatAppShare/SampleChatAppWithShare/UIConstants.h new file mode 100644 index 0000000..ff28af5 --- /dev/null +++ b/Samples/ChatAppShare/SampleChatAppWithShare/UIConstants.h @@ -0,0 +1,23 @@ +#pragma once + +#include + +// Include resource.h for resource constants +#include "resource.h" + +// Modern UI Color Scheme +#define COLOR_PRIMARY RGB(64, 128, 255) // Modern blue +#define COLOR_PRIMARY_DARK RGB(45, 100, 220) // Darker blue for hover +#define COLOR_APP_BACKGROUND RGB(248, 249, 250) // Light gray background +#define COLOR_SURFACE RGB(255, 255, 255) // White surface +#define COLOR_TEXT_PRIMARY RGB(33, 37, 41) // Dark text +#define COLOR_TEXT_SECONDARY RGB(108, 117, 125) // Gray text +#define COLOR_BORDER RGB(222, 226, 230) // Light border +#define COLOR_HOVER RGB(248, 249, 250) // Hover background +#define COLOR_CHAT_BUBBLE_OUT RGB(0, 123, 255) // Outgoing message +#define COLOR_CHAT_BUBBLE_IN RGB(233, 236, 239) // Incoming message + +// UI Constants +#define MAX_LOADSTRING 100 +#define CONTACT_ITEM_HEIGHT 72 +#define AVATAR_SIZE 40 \ No newline at end of file diff --git a/Samples/ChatAppShare/SampleChatAppWithShare/UIManager.cpp b/Samples/ChatAppShare/SampleChatAppWithShare/UIManager.cpp new file mode 100644 index 0000000..bdd4808 --- /dev/null +++ b/Samples/ChatAppShare/SampleChatAppWithShare/UIManager.cpp @@ -0,0 +1,142 @@ +#include "UIManager.h" +#include "UIConstants.h" +#include "ModernUI.h" +#include "ChatModels.h" +#include "FileManager.h" +#include "WindowProcs.h" +#include + +#pragma comment(lib, "comctl32.lib") + +// External declaration - hInst is defined in the main cpp file +extern HINSTANCE hInst; + +// UI Window handles definitions +HWND hContactsList = nullptr; +HWND hChatDisplay = nullptr; +HWND hMessageInput = nullptr; +HWND hSendButton = nullptr; +HWND hContactName = nullptr; +HWND hShareFileButton = nullptr; +HWND hSharedFilesList = nullptr; + +void CreateChatUI(HWND hWnd) +{ + RECT rect; + GetClientRect(hWnd, &rect); + + int width = rect.right - rect.left; + int height = rect.bottom - rect.top; + + // Set window background to modern color + SetClassLongPtr(hWnd, GCLP_HBRBACKGROUND, (LONG_PTR)hBrushBackground); + + // Create contacts list (left panel) with modern styling + hContactsList = CreateWindowEx( + WS_EX_CLIENTEDGE, + L"LISTBOX", NULL, + WS_CHILD | WS_VISIBLE | WS_VSCROLL | LBS_NOTIFY | LBS_OWNERDRAWFIXED, + 20, 20, 280, height - 40, + hWnd, (HMENU)IDC_CONTACTS_LIST, hInst, NULL); + + // Set custom item height for contact list + ::SendMessage(hContactsList, LB_SETITEMHEIGHT, 0, CONTACT_ITEM_HEIGHT); + + // Create contact name label with modern styling + hContactName = CreateWindow(L"STATIC", L"Select a contact to start chatting", + WS_CHILD | WS_VISIBLE | SS_LEFT, + 320, 20, 300, 30, + hWnd, (HMENU)IDC_CONTACT_NAME, hInst, NULL); + + // Create chat display area with modern styling + hChatDisplay = CreateWindowEx( + WS_EX_CLIENTEDGE, + L"EDIT", NULL, + WS_CHILD | WS_VISIBLE | WS_VSCROLL | ES_MULTILINE | ES_READONLY | ES_AUTOVSCROLL, + 320, 60, width - 600, height - 280, + hWnd, (HMENU)IDC_CHAT_DISPLAY, hInst, NULL); + + // Create shared files section header + CreateWindow(L"STATIC", L"Shared Files", + WS_CHILD | WS_VISIBLE | SS_LEFT, + width - 260, 20, 120, 30, + hWnd, NULL, hInst, NULL); + + // Create shared files list with modern styling + hSharedFilesList = CreateWindowEx( + WS_EX_CLIENTEDGE, + L"LISTBOX", NULL, + WS_CHILD | WS_VISIBLE | WS_VSCROLL | LBS_NOTIFY, + width - 260, 60, 240, height - 280, + hWnd, (HMENU)IDC_SHARED_FILES_LIST, hInst, NULL); + + // Create message input with placeholder styling + hMessageInput = CreateWindowEx( + WS_EX_CLIENTEDGE, + L"EDIT", NULL, + WS_CHILD | WS_VISIBLE | ES_MULTILINE | ES_AUTOVSCROLL, + 320, height - 200, width - 600, 60, + hWnd, (HMENU)IDC_MESSAGE_INPUT, hInst, NULL); + + // Create modern styled buttons + hSendButton = CreateWindow(L"BUTTON", L"Send", + WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | BS_OWNERDRAW, + 320, height - 130, 100, 45, + hWnd, (HMENU)IDC_SEND_BUTTON, hInst, NULL); + + hShareFileButton = CreateWindow(L"BUTTON", L"Share File", + WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | BS_OWNERDRAW, + 440, height - 130, 120, 45, + hWnd, (HMENU)IDC_SHARE_FILE_BUTTON, hInst, NULL); + + // Populate contacts list + for (const auto& contact : contacts) { + ::SendMessage(hContactsList, LB_ADDSTRING, 0, (LPARAM)contact.name.c_str()); + } + + // Apply modern fonts to controls + ::SendMessage(hContactName, WM_SETFONT, (WPARAM)hFontTitle, TRUE); + ::SendMessage(hChatDisplay, WM_SETFONT, (WPARAM)hFontRegular, TRUE); + ::SendMessage(hMessageInput, WM_SETFONT, (WPARAM)hFontRegular, TRUE); + ::SendMessage(hSharedFilesList, WM_SETFONT, (WPARAM)hFontRegular, TRUE); + + // Subclass buttons for custom drawing + SetupCustomWindowProcs(); + + // Set modern background colors + SetupWindowColors(); +} + +void ResizeChatUI(HWND hWnd) +{ + RECT rect; + GetClientRect(hWnd, &rect); + int width = rect.right - rect.left; + int height = rect.bottom - rect.top; + + if (hContactsList) { + SetWindowPos(hContactsList, NULL, 20, 20, 280, height - 40, SWP_NOZORDER); + } + if (hChatDisplay) { + SetWindowPos(hChatDisplay, NULL, 320, 60, width - 600, height - 280, SWP_NOZORDER); + } + if (hSharedFilesList) { + SetWindowPos(hSharedFilesList, NULL, width - 260, 60, 240, height - 280, SWP_NOZORDER); + } + if (hMessageInput) { + SetWindowPos(hMessageInput, NULL, 320, height - 200, width - 600, 60, SWP_NOZORDER); + } + if (hSendButton) { + SetWindowPos(hSendButton, NULL, 320, height - 130, 100, 45, SWP_NOZORDER); + } + if (hShareFileButton) { + SetWindowPos(hShareFileButton, NULL, 440, height - 130, 120, 45, SWP_NOZORDER); + } +} + +void SetupWindowColors() +{ + SetClassLongPtr(hChatDisplay, GCLP_HBRBACKGROUND, (LONG_PTR)hBrushSurface); + SetClassLongPtr(hMessageInput, GCLP_HBRBACKGROUND, (LONG_PTR)hBrushSurface); + SetClassLongPtr(hSharedFilesList, GCLP_HBRBACKGROUND, (LONG_PTR)hBrushSurface); +} \ No newline at end of file diff --git a/Samples/ChatAppShare/SampleChatAppWithShare/UIManager.h b/Samples/ChatAppShare/SampleChatAppWithShare/UIManager.h new file mode 100644 index 0000000..74c2a14 --- /dev/null +++ b/Samples/ChatAppShare/SampleChatAppWithShare/UIManager.h @@ -0,0 +1,17 @@ +#pragma once + +#include + +// UI Window handles +extern HWND hContactsList; +extern HWND hChatDisplay; +extern HWND hMessageInput; +extern HWND hSendButton; +extern HWND hContactName; +extern HWND hShareFileButton; +extern HWND hSharedFilesList; + +// UI management functions +void CreateChatUI(HWND hWnd); +void ResizeChatUI(HWND hWnd); +void SetupWindowColors(); \ No newline at end of file diff --git a/Samples/ChatAppShare/SampleChatAppWithShare/WindowProcs.cpp b/Samples/ChatAppShare/SampleChatAppWithShare/WindowProcs.cpp new file mode 100644 index 0000000..ce00862 --- /dev/null +++ b/Samples/ChatAppShare/SampleChatAppWithShare/WindowProcs.cpp @@ -0,0 +1,168 @@ +#include "WindowProcs.h" +#include "UIConstants.h" +#include "ModernUI.h" +#include "ChatModels.h" +#include "UIManager.h" + +// External declarations for UI window handles +extern HWND hSendButton; +extern HWND hShareFileButton; +extern HWND hContactsList; + +// Window procedure storage for subclassing +WNDPROC originalButtonProc = nullptr; +WNDPROC originalListBoxProc = nullptr; + +void SetupCustomWindowProcs() +{ + // Subclass buttons for custom drawing + originalButtonProc = (WNDPROC)SetWindowLongPtr(hSendButton, GWLP_WNDPROC, (LONG_PTR)ModernButtonProc); + SetWindowLongPtr(hShareFileButton, GWLP_WNDPROC, (LONG_PTR)ModernButtonProc); + + // Subclass contact list for custom drawing + originalListBoxProc = (WNDPROC)SetWindowLongPtr(hContactsList, GWLP_WNDPROC, (LONG_PTR)ModernListBoxProc); +} + +// Modern button subclass procedure +LRESULT CALLBACK ModernButtonProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + static bool isHovered = false; + static bool isPressed = false; + + switch (msg) + { + case WM_PAINT: + { + PAINTSTRUCT ps; + HDC hdc = BeginPaint(hWnd, &ps); + + RECT rect; + GetClientRect(hWnd, &rect); + + WCHAR text[256]; + GetWindowText(hWnd, text, 256); + + DrawModernButton(hdc, rect, std::wstring(text), isHovered, isPressed); + + EndPaint(hWnd, &ps); + return 0; + } + + case WM_MOUSEMOVE: + if (!isHovered) + { + isHovered = true; + InvalidateRect(hWnd, NULL, FALSE); + + TRACKMOUSEEVENT tme = {}; + tme.cbSize = sizeof(tme); + tme.dwFlags = TME_LEAVE; + tme.hwndTrack = hWnd; + TrackMouseEvent(&tme); + } + break; + + case WM_MOUSELEAVE: + isHovered = false; + InvalidateRect(hWnd, NULL, FALSE); + break; + + case WM_LBUTTONDOWN: + isPressed = true; + InvalidateRect(hWnd, NULL, FALSE); + SetCapture(hWnd); + break; + + case WM_LBUTTONUP: + if (isPressed) + { + isPressed = false; + InvalidateRect(hWnd, NULL, FALSE); + ReleaseCapture(); + + // Send click notification to parent + HWND hParent = GetParent(hWnd); + int controlId = GetDlgCtrlID(hWnd); + ::SendMessage(hParent, WM_COMMAND, MAKEWPARAM(controlId, BN_CLICKED), (LPARAM)hWnd); + } + break; + } + + return CallWindowProc(originalButtonProc, hWnd, msg, wParam, lParam); +} + +// Modern listbox subclass procedure +LRESULT CALLBACK ModernListBoxProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + switch (msg) + { + case WM_LBUTTONDOWN: + if (hWnd == hContactsList) + { + // Handle mouse click to ensure proper contact selection with scrolling + POINT pt = { LOWORD(lParam), HIWORD(lParam) }; + + // Calculate which contact was clicked based on the scroll position + int topIndex = (int)::SendMessage(hWnd, LB_GETTOPINDEX, 0, 0); + int clickedVisiblePos = pt.y / CONTACT_ITEM_HEIGHT; + int actualContactIndex = topIndex + clickedVisiblePos; + + // Ensure the clicked contact is valid + if (actualContactIndex >= 0 && actualContactIndex < (int)contacts.size()) + { + // Set the correct selection in the listbox + ::SendMessage(hWnd, LB_SETCURSEL, actualContactIndex, 0); + + // Trigger the selection change event to update the UI + HWND hParent = GetParent(hWnd); + int controlId = GetDlgCtrlID(hWnd); + ::SendMessage(hParent, WM_COMMAND, MAKEWPARAM(controlId, LBN_SELCHANGE), (LPARAM)hWnd); + + return 0; // We handled the click + } + } + break; + + case WM_PAINT: + if (hWnd == hContactsList) + { + PAINTSTRUCT ps; + HDC hdc = BeginPaint(hWnd, &ps); + + RECT clientRect; + GetClientRect(hWnd, &clientRect); + + // Fill background + FillRect(hdc, &clientRect, hBrushSurface); + + int itemCount = (int)contacts.size(); + int selectedIndex = (int)::SendMessage(hWnd, LB_GETCURSEL, 0, 0); + + // Get the first visible item index to handle scrolling correctly + int topIndex = (int)::SendMessage(hWnd, LB_GETTOPINDEX, 0, 0); + + // Calculate how many items can be visible + int visibleItemCount = (clientRect.bottom / CONTACT_ITEM_HEIGHT) + 1; + + // Draw only the visible items + for (int visiblePos = 0; visiblePos < visibleItemCount; visiblePos++) + { + int actualIndex = topIndex + visiblePos; + + // Stop if we've reached the end of the contact list + if (actualIndex >= itemCount) break; + + RECT itemRect = {0, visiblePos * CONTACT_ITEM_HEIGHT, clientRect.right, (visiblePos + 1) * CONTACT_ITEM_HEIGHT}; + + // Draw the contact that should be visible at this position + DrawContactItem(hdc, itemRect, contacts[actualIndex], actualIndex == selectedIndex); + } + + EndPaint(hWnd, &ps); + return 0; + } + break; + } + + return CallWindowProc(originalListBoxProc, hWnd, msg, wParam, lParam); +} \ No newline at end of file diff --git a/Samples/ChatAppShare/SampleChatAppWithShare/WindowProcs.h b/Samples/ChatAppShare/SampleChatAppWithShare/WindowProcs.h new file mode 100644 index 0000000..97c8623 --- /dev/null +++ b/Samples/ChatAppShare/SampleChatAppWithShare/WindowProcs.h @@ -0,0 +1,14 @@ +#pragma once + +#include + +// Window procedures +LRESULT CALLBACK ModernButtonProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); +LRESULT CALLBACK ModernListBoxProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); + +// Original window procedures storage +extern WNDPROC originalButtonProc; +extern WNDPROC originalListBoxProc; + +// Setup functions +void SetupCustomWindowProcs(); \ No newline at end of file diff --git a/Samples/ChatAppShare/SampleChatAppWithShare/framework.h b/Samples/ChatAppShare/SampleChatAppWithShare/framework.h new file mode 100644 index 0000000..414c103 --- /dev/null +++ b/Samples/ChatAppShare/SampleChatAppWithShare/framework.h @@ -0,0 +1,29 @@ +// header.h : include file for standard system include files, +// or project specific include files +// + +#pragma once + +#include "targetver.h" +#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers + +// Windows Header Files +#include + +// WinRT Headers for Share Target functionality +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// C RunTime Header Files +#include +#include +#include +#include diff --git a/Samples/ChatAppShare/SampleChatAppWithShare/small.ico b/Samples/ChatAppShare/SampleChatAppWithShare/small.ico new file mode 100644 index 0000000000000000000000000000000000000000..b3ec03bd617f32e58128fa977fd6ac9605124f4b GIT binary patch literal 46227 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 From a56d29b08c7d11481aa4d854e03105aaf090ca96 Mon Sep 17 00:00:00 2001 From: Ravish Roshan Date: Fri, 21 Nov 2025 05:24:45 +0530 Subject: [PATCH 13/20] Push share app changes. --- Samples/ChatAppShare/ShareApp/ChatManager.cpp | 132 +++++ Samples/ChatAppShare/ShareApp/ChatManager.h | 10 + Samples/ChatAppShare/ShareApp/ChatModels.cpp | 48 ++ Samples/ChatAppShare/ShareApp/ChatModels.h | 32 ++ .../ShareApp/ContactSelectionDialog.cpp | 275 +++++++++++ .../ShareApp/ContactSelectionDialog.h | 45 ++ Samples/ChatAppShare/ShareApp/FileManager.cpp | 154 ++++++ Samples/ChatAppShare/ShareApp/FileManager.h | 13 + Samples/ChatAppShare/ShareApp/ModernUI.cpp | 128 +++++ Samples/ChatAppShare/ShareApp/ModernUI.h | 21 + .../ChatAppShare/ShareApp/PackageIdentity.cpp | 463 ++++++++++++++++++ .../ChatAppShare/ShareApp/PackageIdentity.h | 23 + Samples/ChatAppShare/ShareApp/Resource.h | 48 ++ Samples/ChatAppShare/ShareApp/ShareApp.cpp | 457 +++++++++++++++++ .../ShareApp/ShareApp.exe.manifest | 9 + Samples/ChatAppShare/ShareApp/ShareApp.h | 3 + Samples/ChatAppShare/ShareApp/ShareApp.ico | Bin 0 -> 46227 bytes Samples/ChatAppShare/ShareApp/ShareApp.rc | Bin 0 -> 9156 bytes .../ChatAppShare/ShareApp/ShareApp.vcxproj | 234 +++++++++ .../ShareApp/ShareApp.vcxproj.filters | 118 +++++ .../ShareApp/ShareTargetManager.cpp | 425 ++++++++++++++++ .../ShareApp/ShareTargetManager.h | 33 ++ Samples/ChatAppShare/ShareApp/UIConstants.h | 23 + Samples/ChatAppShare/ShareApp/UIManager.cpp | 142 ++++++ Samples/ChatAppShare/ShareApp/UIManager.h | 17 + Samples/ChatAppShare/ShareApp/WindowProcs.cpp | 168 +++++++ Samples/ChatAppShare/ShareApp/WindowProcs.h | 14 + Samples/ChatAppShare/ShareApp/framework.h | 29 ++ Samples/ChatAppShare/ShareApp/small.ico | Bin 0 -> 46227 bytes Samples/ChatAppShare/ShareApp/targetver.h | 6 + 30 files changed, 3070 insertions(+) create mode 100644 Samples/ChatAppShare/ShareApp/ChatManager.cpp create mode 100644 Samples/ChatAppShare/ShareApp/ChatManager.h create mode 100644 Samples/ChatAppShare/ShareApp/ChatModels.cpp create mode 100644 Samples/ChatAppShare/ShareApp/ChatModels.h create mode 100644 Samples/ChatAppShare/ShareApp/ContactSelectionDialog.cpp create mode 100644 Samples/ChatAppShare/ShareApp/ContactSelectionDialog.h create mode 100644 Samples/ChatAppShare/ShareApp/FileManager.cpp create mode 100644 Samples/ChatAppShare/ShareApp/FileManager.h create mode 100644 Samples/ChatAppShare/ShareApp/ModernUI.cpp create mode 100644 Samples/ChatAppShare/ShareApp/ModernUI.h create mode 100644 Samples/ChatAppShare/ShareApp/PackageIdentity.cpp create mode 100644 Samples/ChatAppShare/ShareApp/PackageIdentity.h create mode 100644 Samples/ChatAppShare/ShareApp/Resource.h create mode 100644 Samples/ChatAppShare/ShareApp/ShareApp.cpp create mode 100644 Samples/ChatAppShare/ShareApp/ShareApp.exe.manifest create mode 100644 Samples/ChatAppShare/ShareApp/ShareApp.h create mode 100644 Samples/ChatAppShare/ShareApp/ShareApp.ico create mode 100644 Samples/ChatAppShare/ShareApp/ShareApp.rc create mode 100644 Samples/ChatAppShare/ShareApp/ShareApp.vcxproj create mode 100644 Samples/ChatAppShare/ShareApp/ShareApp.vcxproj.filters create mode 100644 Samples/ChatAppShare/ShareApp/ShareTargetManager.cpp create mode 100644 Samples/ChatAppShare/ShareApp/ShareTargetManager.h create mode 100644 Samples/ChatAppShare/ShareApp/UIConstants.h create mode 100644 Samples/ChatAppShare/ShareApp/UIManager.cpp create mode 100644 Samples/ChatAppShare/ShareApp/UIManager.h create mode 100644 Samples/ChatAppShare/ShareApp/WindowProcs.cpp create mode 100644 Samples/ChatAppShare/ShareApp/WindowProcs.h create mode 100644 Samples/ChatAppShare/ShareApp/framework.h create mode 100644 Samples/ChatAppShare/ShareApp/small.ico create mode 100644 Samples/ChatAppShare/ShareApp/targetver.h diff --git a/Samples/ChatAppShare/ShareApp/ChatManager.cpp b/Samples/ChatAppShare/ShareApp/ChatManager.cpp new file mode 100644 index 0000000..b00ebb6 --- /dev/null +++ b/Samples/ChatAppShare/ShareApp/ChatManager.cpp @@ -0,0 +1,132 @@ +#include "ChatManager.h" +#include "ChatModels.h" +#include "FileManager.h" +#include "UIManager.h" +#include +#include +#include + +void LoadContactChat(int contactIndex) +{ + if (!IsValidContactIndex(contactIndex)) return; + + selectedContactIndex = contactIndex; + const Contact& contact = contacts[contactIndex]; + + // Update contact name with status indicator + std::wstring headerText = contact.name + L" " + L" (" + contact.status + L")"; + SetWindowText(hContactName, headerText.c_str()); + + // Clear and populate chat display with better formatting + SetWindowText(hChatDisplay, L""); + + std::wstring chatText; + for (const auto& message : contact.messages) { + // Add timestamps and better message formatting + SYSTEMTIME st; + GetLocalTime(&st); + WCHAR timeStr[50]; + swprintf_s(timeStr, 50, L"[%02d:%02d] ", st.wHour, st.wMinute); + + if (message.find(L"You:") == 0) { + chatText += L" "; // Right align for your messages + chatText += timeStr + message + L"\r\n\r\n"; + } else { + chatText += timeStr + message + L"\r\n\r\n"; + } + } + + SetWindowText(hChatDisplay, chatText.c_str()); + + // Scroll to bottom + ::SendMessage(hChatDisplay, EM_SETSEL, -1, -1); + ::SendMessage(hChatDisplay, EM_SCROLLCARET, 0, 0); + + // Update shared files list + UpdateSharedFilesList(); + + // Refresh contact list to show selection + InvalidateRect(hContactsList, NULL, TRUE); +} + +void AddMessageToChat(const std::wstring& message, bool isOutgoing) +{ + Contact* contact = GetSelectedContact(); + if (!contact) return; + + // Add timestamp to message + SYSTEMTIME st; + GetLocalTime(&st); + WCHAR timeStr[50]; + swprintf_s(timeStr, 50, L"[%02d:%02d] ", st.wHour, st.wMinute); + + std::wstring formattedMessage; + if (isOutgoing) { + formattedMessage = L"You: " + message + L" ??"; + } else { + formattedMessage = contact->name + L": " + message; + } + + contact->messages.push_back(formattedMessage); + contact->lastMessage = message.length() > 50 ? message.substr(0, 47) + L"..." : message; + + // Update chat display + LoadContactChat(selectedContactIndex); + + // Refresh contacts list to update last message preview + InvalidateRect(hContactsList, NULL, TRUE); +} + +void SendChatMessage() +{ + if (selectedContactIndex < 0) return; + + WCHAR buffer[1024]; + GetWindowText(hMessageInput, buffer, 1024); + + std::wstring message(buffer); + if (!message.empty()) { + AddMessageToChat(message, true); + SetWindowText(hMessageInput, L""); + + // Simulate auto-reply after a short delay + SetTimer(GetParent(hMessageInput), 1, 2000, NULL); + } +} + +void ProcessAutoReply(HWND hWnd, int timerType) +{ + if (selectedContactIndex < 0) return; + + KillTimer(hWnd, timerType); + + if (timerType == 1) { + // Auto-reply from the selected contact with more variety + std::vector autoReplies = { + L"Got it!", + L"Thanks for letting me know!", + L"Sounds good!", + L"I'll get back to you soon.", + L"Perfect!", + L"Absolutely!", + L"Let me think about it.", + L"Great idea!" + }; + + int replyIndex = rand() % autoReplies.size(); + AddMessageToChat(autoReplies[replyIndex], false); + } + else if (timerType == 2) { + // Auto-reply acknowledging the shared file + std::vector fileReplies = { + L"Thanks for sharing the file!", + L"Got the file, will check it out.", + L"File received, thanks! ??", + L"Perfect timing, I needed this file.", + L"Awesome, downloading now!" + }; + + int replyIndex = rand() % fileReplies.size(); + AddMessageToChat(fileReplies[replyIndex], false); + } +} \ No newline at end of file diff --git a/Samples/ChatAppShare/ShareApp/ChatManager.h b/Samples/ChatAppShare/ShareApp/ChatManager.h new file mode 100644 index 0000000..daefb88 --- /dev/null +++ b/Samples/ChatAppShare/ShareApp/ChatManager.h @@ -0,0 +1,10 @@ +#pragma once + +#include +#include + +// Chat management functions +void LoadContactChat(int contactIndex); +void SendChatMessage(); +void AddMessageToChat(const std::wstring& message, bool isOutgoing); +void ProcessAutoReply(HWND hWnd, int timerType); \ No newline at end of file diff --git a/Samples/ChatAppShare/ShareApp/ChatModels.cpp b/Samples/ChatAppShare/ShareApp/ChatModels.cpp new file mode 100644 index 0000000..db4f520 --- /dev/null +++ b/Samples/ChatAppShare/ShareApp/ChatModels.cpp @@ -0,0 +1,48 @@ +#include "ChatModels.h" + +// Global data definitions +std::vector contacts; +std::map> chatHistory; +int selectedContactIndex = -1; + +void InitializeContacts() +{ + contacts = { + {L"Alice Johnson", L"Hey, how are you?", {L"Alice: Hey, how are you?", L"You: I'm doing great, thanks!", L"Alice: That's wonderful to hear!"}, {}, L"Available", true}, + {L"Bob Smith", L"See you tomorrow!", {L"Bob: Are we still meeting tomorrow?", L"You: Yes, see you at 3 PM", L"Bob: See you tomorrow!"}, {}, L"In a meeting", true}, + {L"Carol Williams", L"Thanks for the help", {L"Carol: Could you help me with the project?", L"You: Of course! What do you need?", L"Carol: Thanks for the help"}, {}, L"Available", true}, + {L"David Brown", L"Great presentation!", {L"David: Great presentation today!", L"You: Thank you! I'm glad you liked it"}, {}, L"Away", false}, + {L"Emma Davis", L"Coffee later?", {L"Emma: Want to grab coffee later?", L"You: Sure! What time works for you?", L"Emma: Coffee later?"}, {}, L"Available", true}, + {L"Frank Miller", L"Happy Birthday!", {L"Frank: Happy Birthday!", L"You: Thank you so much!"}, {}, L"Busy", true}, + {L"Grace Wilson", L"Meeting rescheduled", {L"Grace: Meeting has been rescheduled to 4 PM", L"You: Got it, thanks for letting me know"}, {}, L"Available", true}, + {L"Henry Taylor", L"Weekend plans?", {L"Henry: Any plans for the weekend?", L"You: Nothing concrete yet", L"Henry: Weekend plans?"}, {}, L"Offline", false}, + {L"Ivy Anderson", L"Project update", {L"Ivy: Here's the project update you requested", L"You: Perfect, reviewing it now"}, {}, L"Available", true}, + {L"Jack Thompson", L"Game night Friday", {L"Jack: Game night this Friday?", L"You: Count me in!", L"Jack: Game night Friday"}, {}, L"Gaming", true}, + {L"Kate Garcia", L"Recipe sharing", {L"Kate: Loved that recipe you shared!", L"You: I'm so glad you enjoyed it!"}, {}, L"Cooking", true}, + {L"Leo Martinez", L"Workout buddy", {L"Leo: Gym session tomorrow morning?", L"You: Absolutely! 7 AM as usual?"}, {}, L"At the gym", true}, + {L"Mia Rodriguez", L"Book recommendation", {L"Mia: Any good book recommendations?", L"You: I just finished a great mystery novel"}, {}, L"Reading", true}, + {L"Noah Lee", L"Tech discussion", {L"Noah: Thoughts on the new framework?", L"You: It looks promising! Want to discuss over lunch?"}, {}, L"Coding", true}, + {L"Olivia Clark", L"Travel planning", {L"Olivia: Planning the vacation itinerary", L"You: Excited to see what you've planned!"}, {}, L"Traveling", false} + }; + + // Add some sample shared files to demonstrate the feature + SYSTEMTIME st; + GetSystemTime(&st); + + contacts[0].sharedFiles.push_back({L"Project_Proposal.docx", L"C:\\Documents\\Project_Proposal.docx", L"Alice", st}); + contacts[1].sharedFiles.push_back({L"Meeting_Notes.pdf", L"C:\\Documents\\Meeting_Notes.pdf", L"Bob", st}); + contacts[2].sharedFiles.push_back({L"Budget_Spreadsheet.xlsx", L"C:\\Documents\\Budget_Spreadsheet.xlsx", L"Carol", st}); +} + +Contact* GetSelectedContact() +{ + if (IsValidContactIndex(selectedContactIndex)) { + return &contacts[selectedContactIndex]; + } + return nullptr; +} + +bool IsValidContactIndex(int index) +{ + return index >= 0 && index < (int)contacts.size(); +} \ No newline at end of file diff --git a/Samples/ChatAppShare/ShareApp/ChatModels.h b/Samples/ChatAppShare/ShareApp/ChatModels.h new file mode 100644 index 0000000..3fb7531 --- /dev/null +++ b/Samples/ChatAppShare/ShareApp/ChatModels.h @@ -0,0 +1,32 @@ +#pragma once + +#include +#include +#include +#include + +struct SharedFile { + std::wstring fileName; + std::wstring filePath; + std::wstring sharedBy; + SYSTEMTIME timeShared; +}; + +struct Contact { + std::wstring name; + std::wstring lastMessage; + std::vector messages; + std::vector sharedFiles; + std::wstring status; + bool isOnline; +}; + +// Global data +extern std::vector contacts; +extern std::map> chatHistory; +extern int selectedContactIndex; + +// Contact management functions +void InitializeContacts(); +Contact* GetSelectedContact(); +bool IsValidContactIndex(int index); \ No newline at end of file diff --git a/Samples/ChatAppShare/ShareApp/ContactSelectionDialog.cpp b/Samples/ChatAppShare/ShareApp/ContactSelectionDialog.cpp new file mode 100644 index 0000000..ba1aeca --- /dev/null +++ b/Samples/ChatAppShare/ShareApp/ContactSelectionDialog.cpp @@ -0,0 +1,275 @@ +#include "ContactSelectionDialog.h" +#include "Resource.h" +#include "UIConstants.h" +#include + +// Static member initialization +ContactSelectionDialog::SelectionResult ContactSelectionDialog::s_dialogResult; +std::wstring ContactSelectionDialog::s_currentFilePath; +std::wstring ContactSelectionDialog::s_currentFileName; + +ContactSelectionDialog::SelectionResult ContactSelectionDialog::ShowContactSelectionDialog(HWND hParent, const std::wstring& filePath, const std::wstring& fileName) +{ + // Store the file information for the dialog + s_currentFilePath = filePath; + s_currentFileName = fileName; + + // Reset the result + s_dialogResult = SelectionResult(); + + // Show the modal dialog + INT_PTR result = DialogBox(GetModuleHandle(NULL), MAKEINTRESOURCE(IDD_CONTACT_SELECTION), hParent, ContactSelectionDlgProc); + + if (result == IDOK) + { + s_dialogResult.wasSelected = true; + s_dialogResult.filePath = s_currentFilePath; + s_dialogResult.fileName = s_currentFileName; + } + else + { + s_dialogResult.wasSelected = false; + } + + return s_dialogResult; +} + +INT_PTR CALLBACK ContactSelectionDialog::ContactSelectionDlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) +{ + switch (message) + { + case WM_INITDIALOG: + { + // Set the dialog title with file name + std::wstring title = L"Share \"" + s_currentFileName + L"\" - Select Contact"; + SetWindowText(hDlg, title.c_str()); + + // Set up the dialog layout and styling + SetupDialogSizing(hDlg); + + // Initialize and populate the contact list + HWND hListBox = GetDlgItem(hDlg, IDC_CONTACT_SELECTION_LIST); + if (hListBox) + { + InitializeContactList(hListBox); + PopulateContactList(hListBox); + } + + // Set up the share message edit control + HWND hMessageEdit = GetDlgItem(hDlg, IDC_SHARE_MESSAGE_EDIT); + if (hMessageEdit) + { + std::wstring defaultMessage = L"I'm sharing \"" + s_currentFileName + L"\" with you!"; + SetWindowText(hMessageEdit, defaultMessage.c_str()); + } + + // Initially disable the Select button until a contact is chosen + EnableWindow(GetDlgItem(hDlg, IDC_SELECT_CONTACT_BUTTON), FALSE); + + // Center the dialog on the parent + RECT rcParent, rcDlg; + HWND hParent = GetParent(hDlg); + if (hParent) + { + GetWindowRect(hParent, &rcParent); + GetWindowRect(hDlg, &rcDlg); + + int x = rcParent.left + (rcParent.right - rcParent.left - (rcDlg.right - rcDlg.left)) / 2; + int y = rcParent.top + (rcParent.bottom - rcParent.top - (rcDlg.bottom - rcDlg.top)) / 2; + + SetWindowPos(hDlg, HWND_TOP, x, y, 0, 0, SWP_NOSIZE); + } + + return TRUE; + } + + case WM_COMMAND: + switch (LOWORD(wParam)) + { + case IDC_CONTACT_SELECTION_LIST: + if (HIWORD(wParam) == LBN_SELCHANGE) + { + int selectedIndex = (int)SendDlgItemMessage(hDlg, IDC_CONTACT_SELECTION_LIST, LB_GETCURSEL, 0, 0); + HandleContactSelection(hDlg, selectedIndex); + } + else if (HIWORD(wParam) == LBN_DBLCLK) + { + // Double-click selects and closes dialog + OnSelectContact(hDlg); + } + break; + + case IDC_SELECT_CONTACT_BUTTON: + OnSelectContact(hDlg); + break; + + case IDC_CANCEL_SELECTION_BUTTON: + case IDCANCEL: + OnCancel(hDlg); + break; + + case IDOK: + OnSelectContact(hDlg); + break; + } + break; + + case WM_CLOSE: + OnCancel(hDlg); + break; + } + + return FALSE; +} + +void ContactSelectionDialog::InitializeContactList(HWND hListBox) +{ + // Set up the list box for contact display + SendMessage(hListBox, LB_RESETCONTENT, 0, 0); +} + +void ContactSelectionDialog::PopulateContactList(HWND hListBox) +{ + // Clear existing items + SendMessage(hListBox, LB_RESETCONTENT, 0, 0); + + // Ensure contacts are initialized + if (contacts.empty()) + { + InitializeContacts(); + } + + // Debug logging + OutputDebugStringW((L"ContactSelectionDialog: Populating list with " + std::to_wstring(contacts.size()) + L" contacts\n").c_str()); + + // Add all contacts to the list + for (size_t i = 0; i < contacts.size(); ++i) + { + const Contact& contact = contacts[i]; + + // Create display text with contact name and status + std::wstring displayText = contact.name + L" - " + contact.status; + if (!contact.isOnline) + { + displayText += L" (Offline)"; + } + + int itemIndex = (int)SendMessage(hListBox, LB_ADDSTRING, 0, (LPARAM)displayText.c_str()); + + // Store the contact index as item data + SendMessage(hListBox, LB_SETITEMDATA, itemIndex, (LPARAM)i); + + // Debug log each contact being added + OutputDebugStringW((L"ContactSelectionDialog: Added contact " + std::to_wstring(i) + L": " + contact.name + L"\n").c_str()); + } + + // If no contacts were added, add a placeholder + if (contacts.empty()) + { + SendMessage(hListBox, LB_ADDSTRING, 0, (LPARAM)L"No contacts available"); + OutputDebugStringW(L"ContactSelectionDialog: No contacts available - added placeholder\n"); + } + else + { + OutputDebugStringW((L"ContactSelectionDialog: Successfully populated " + std::to_wstring(contacts.size()) + L" contacts\n").c_str()); + } +} + +void ContactSelectionDialog::HandleContactSelection(HWND hDlg, int selectedIndex) +{ + if (selectedIndex != LB_ERR) + { + // Enable the Select button when a contact is selected + EnableWindow(GetDlgItem(hDlg, IDC_SELECT_CONTACT_BUTTON), TRUE); + + // Get the contact index from item data + HWND hListBox = GetDlgItem(hDlg, IDC_CONTACT_SELECTION_LIST); + int contactIndex = (int)SendMessage(hListBox, LB_GETITEMDATA, selectedIndex, 0); + + if (contactIndex >= 0 && contactIndex < (int)contacts.size()) + { + // Update the share message with the selected contact's name + const Contact& contact = contacts[contactIndex]; + std::wstring personalizedMessage = L"Hey " + contact.name + L"! I'm sharing \"" + s_currentFileName + L"\" with you."; + + HWND hMessageEdit = GetDlgItem(hDlg, IDC_SHARE_MESSAGE_EDIT); + if (hMessageEdit) + { + SetWindowText(hMessageEdit, personalizedMessage.c_str()); + } + } + } + else + { + // Disable the Select button when no contact is selected + EnableWindow(GetDlgItem(hDlg, IDC_SELECT_CONTACT_BUTTON), FALSE); + } +} + +void ContactSelectionDialog::OnSelectContact(HWND hDlg) +{ + HWND hListBox = GetDlgItem(hDlg, IDC_CONTACT_SELECTION_LIST); + int selectedIndex = (int)SendMessage(hListBox, LB_GETCURSEL, 0, 0); + + if (selectedIndex == LB_ERR) + { + MessageBox(hDlg, L"Please select a contact to share with.", L"No Contact Selected", MB_OK | MB_ICONWARNING); + return; + } + + // Check if contacts are available + if (contacts.empty()) + { + MessageBox(hDlg, L"No contacts are available. Please ensure the application is properly initialized.", L"No Contacts Available", MB_OK | MB_ICONWARNING); + return; + } + + // Get the contact index from item data + int contactIndex = (int)SendMessage(hListBox, LB_GETITEMDATA, selectedIndex, 0); + + // Debug logging + OutputDebugStringW((L"ContactSelectionDialog: Selected contact index: " + std::to_wstring(contactIndex) + L", total contacts: " + std::to_wstring(contacts.size()) + L"\n").c_str()); + + if (contactIndex >= 0 && contactIndex < (int)contacts.size()) + { + // Get the share message + HWND hMessageEdit = GetDlgItem(hDlg, IDC_SHARE_MESSAGE_EDIT); + WCHAR messageBuffer[512] = {0}; + if (hMessageEdit) + { + GetWindowText(hMessageEdit, messageBuffer, 512); + } + + // Store the result + s_dialogResult.contactIndex = contactIndex; + s_dialogResult.shareMessage = messageBuffer; + + // Debug logging + OutputDebugStringW((L"ContactSelectionDialog: Contact selected - " + contacts[contactIndex].name + L"\n").c_str()); + + // Close dialog with success + EndDialog(hDlg, IDOK); + } + else + { + MessageBox(hDlg, L"Invalid contact selection. Please try again.", L"Selection Error", MB_OK | MB_ICONERROR); + OutputDebugStringW((L"ContactSelectionDialog: Invalid contact index: " + std::to_wstring(contactIndex) + L"\n").c_str()); + } +} + +void ContactSelectionDialog::OnCancel(HWND hDlg) +{ + // Close dialog with cancel + EndDialog(hDlg, IDCANCEL); +} + +void ContactSelectionDialog::SetupDialogSizing(HWND hDlg) +{ + // Set dialog size (approximately 400x500 pixels) + SetWindowPos(hDlg, NULL, 0, 0, 420, 520, SWP_NOMOVE | SWP_NOZORDER); +} + +void ContactSelectionDialog::ApplyModernStyling(HWND hDlg) +{ + // Modern styling is optional for now - skip to avoid dependencies +} \ No newline at end of file diff --git a/Samples/ChatAppShare/ShareApp/ContactSelectionDialog.h b/Samples/ChatAppShare/ShareApp/ContactSelectionDialog.h new file mode 100644 index 0000000..fc06ffe --- /dev/null +++ b/Samples/ChatAppShare/ShareApp/ContactSelectionDialog.h @@ -0,0 +1,45 @@ +#pragma once + +#include +#include +#include +#include "ChatModels.h" + +// Contact Selection Dialog Manager +class ContactSelectionDialog +{ +public: + // Structure to hold the result of contact selection + struct SelectionResult + { + bool wasSelected; + int contactIndex; + std::wstring shareMessage; + std::wstring filePath; + std::wstring fileName; + + SelectionResult() : wasSelected(false), contactIndex(-1) {} + }; + + // Show the contact selection dialog + static SelectionResult ShowContactSelectionDialog(HWND hParent, const std::wstring& filePath, const std::wstring& fileName); + +private: + // Dialog procedure for contact selection + static INT_PTR CALLBACK ContactSelectionDlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam); + + // Helper functions + static void InitializeContactList(HWND hListBox); + static void PopulateContactList(HWND hListBox); + static void UpdateContactListDisplay(HWND hListBox); + static void HandleContactSelection(HWND hDlg, int selectedIndex); + static void OnSelectContact(HWND hDlg); + static void OnCancel(HWND hDlg); + static void SetupDialogSizing(HWND hDlg); + static void ApplyModernStyling(HWND hDlg); + + // Static data for dialog communication + static SelectionResult s_dialogResult; + static std::wstring s_currentFilePath; + static std::wstring s_currentFileName; +}; \ No newline at end of file diff --git a/Samples/ChatAppShare/ShareApp/FileManager.cpp b/Samples/ChatAppShare/ShareApp/FileManager.cpp new file mode 100644 index 0000000..31ace5f --- /dev/null +++ b/Samples/ChatAppShare/ShareApp/FileManager.cpp @@ -0,0 +1,154 @@ +#include "FileManager.h" +#include "ChatModels.h" +#include "ChatManager.h" +#include "UIManager.h" +#include +#include + +#pragma comment(lib, "comdlg32.lib") + +// External declarations for UI window handles +extern HWND hSharedFilesList; +extern HWND hContactsList; + +void ShareFile() +{ + // Check if a contact is selected first + if (selectedContactIndex < 0) { + MessageBox(NULL, L"Please select a contact to share files with.", L"No Contact Selected", MB_OK | MB_ICONWARNING); + return; + } + + // Get the main window handle (parent of the share file button) + HWND hMainWindow = GetParent(hSharedFilesList); + while (GetParent(hMainWindow)) + { + hMainWindow = GetParent(hMainWindow); + } + + OPENFILENAME ofn; + WCHAR szFile[260] = {0}; + + ZeroMemory(&ofn, sizeof(ofn)); + ofn.lStructSize = sizeof(ofn); + ofn.hwndOwner = hMainWindow; // Set the main window as owner + ofn.lpstrFile = szFile; + ofn.nMaxFile = sizeof(szFile); + ofn.lpstrFilter = L"All Files\0*.*\0?? Text Files\0*.TXT\0?? Document Files\0*.DOC;*.DOCX\0??? Image Files\0*.BMP;*.JPG;*.PNG;*.GIF\0?? PDF Files\0*.PDF\0?? Excel Files\0*.XLS;*.XLSX\0"; + ofn.nFilterIndex = 1; + ofn.lpstrFileTitle = NULL; + ofn.nMaxFileTitle = 0; + ofn.lpstrInitialDir = NULL; + ofn.lpstrTitle = L"Select File to Share"; + ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_EXPLORER; + + if (GetOpenFileName(&ofn)) + { + // Extract file name from full path + std::wstring fullPath(szFile); + size_t lastSlash = fullPath.find_last_of(L"\\"); + std::wstring fileName; + if (lastSlash != std::wstring::npos) + { + fileName = fullPath.substr(lastSlash + 1); + } + else + { + fileName = fullPath; + } + + // Create shared file entry + SharedFile newFile; + newFile.fileName = fileName; + newFile.filePath = fullPath; + newFile.sharedBy = L"You"; + GetSystemTime(&newFile.timeShared); + + // Add file to the currently selected contact's shared files + if (selectedContactIndex >= 0 && selectedContactIndex < (int)contacts.size()) + { + contacts[selectedContactIndex].sharedFiles.push_back(newFile); + + // Add file sharing notification to chat + std::wstring fileShareMsg = L"?? Shared file: " + fileName; + AddMessageToChat(fileShareMsg, true); + + // Update UI + AddSharedFileToChat(newFile, true); + + // Update contacts list display to show the shared activity + InvalidateRect(hContactsList, NULL, TRUE); + + // Show success message with current contact + std::wstring successMsg = L"File \"" + fileName + L"\" has been shared with " + contacts[selectedContactIndex].name + L"!"; + MessageBox(hMainWindow, successMsg.c_str(), L"File Shared Successfully", MB_OK | MB_ICONINFORMATION); + + // Simulate auto-reply from contact acknowledging the file + SetTimer(hMainWindow, 2, 2000, NULL); + } + } +} + +void AddSharedFileToChat(const SharedFile& file, bool isOutgoing) +{ + Contact* contact = GetSelectedContact(); + if (!contact) return; + + std::wstring sharer = isOutgoing ? L"You" : contact->name; + + // Format file sharing message with timestamp + SYSTEMTIME st; + GetLocalTime(&st); + WCHAR timeStr[50]; + swprintf_s(timeStr, 50, L"[%02d:%02d] ", st.wHour, st.wMinute); + + std::wstring shareMessage = sharer + L" shared: " + file.fileName + L" ??"; + contact->messages.push_back(shareMessage); + + // Update last message preview + contact->lastMessage = L"?? " + file.fileName; + + // Add to shared files list + contact->sharedFiles.push_back(file); + + // Refresh UI + LoadContactChat(selectedContactIndex); + UpdateSharedFilesList(); +} + +void OpenSharedFile(int fileIndex) +{ + Contact* contact = GetSelectedContact(); + if (!contact || fileIndex < 0 || fileIndex >= (int)contact->sharedFiles.size()) { + return; + } + + const SharedFile& file = contact->sharedFiles[fileIndex]; + + // Try to open the file with the default application + HINSTANCE result = ShellExecute(NULL, L"open", file.filePath.c_str(), NULL, NULL, SW_SHOWNORMAL); + + if ((intptr_t)result <= 32) { + // If opening failed, show file location in explorer + std::wstring explorerCmd = L"/select,\"" + file.filePath + L"\""; + ShellExecute(NULL, L"open", L"explorer.exe", explorerCmd.c_str(), NULL, SW_SHOWNORMAL); + } +} + +void UpdateSharedFilesList() +{ + if (!hSharedFilesList) return; + + Contact* contact = GetSelectedContact(); + + // Clear the list + SendMessage(hSharedFilesList, LB_RESETCONTENT, 0, 0); + + if (!contact) return; + + // Add shared files to the list + for (const auto& file : contact->sharedFiles) { + std::wstring displayText = file.fileName + L" (shared by " + file.sharedBy + L")"; + SendMessage(hSharedFilesList, LB_ADDSTRING, 0, (LPARAM)displayText.c_str()); + } +} \ No newline at end of file diff --git a/Samples/ChatAppShare/ShareApp/FileManager.h b/Samples/ChatAppShare/ShareApp/FileManager.h new file mode 100644 index 0000000..e28e4db --- /dev/null +++ b/Samples/ChatAppShare/ShareApp/FileManager.h @@ -0,0 +1,13 @@ +#pragma once + +#include +#include +#include "ChatModels.h" + +// File management functions +void ShareFile(); +void AddSharedFileToChat(const SharedFile& file, bool isOutgoing); +void UpdateSharedFilesList(); +void OpenSharedFile(int fileIndex); +std::wstring GetFileExtensionIcon(const std::wstring& filePath); +std::wstring FormatFileSize(DWORD fileSize); \ No newline at end of file diff --git a/Samples/ChatAppShare/ShareApp/ModernUI.cpp b/Samples/ChatAppShare/ShareApp/ModernUI.cpp new file mode 100644 index 0000000..ca30eb2 --- /dev/null +++ b/Samples/ChatAppShare/ShareApp/ModernUI.cpp @@ -0,0 +1,128 @@ +#include "ModernUI.h" +#include "UIConstants.h" +#include + +#pragma comment(lib, "gdiplus.lib") + +// Modern UI Variables definitions +HBRUSH hBrushBackground = nullptr; +HBRUSH hBrushSurface = nullptr; +HBRUSH hBrushPrimary = nullptr; +HBRUSH hBrushHover = nullptr; +HFONT hFontRegular = nullptr; +HFONT hFontBold = nullptr; +HFONT hFontTitle = nullptr; +HPEN hPenBorder = nullptr; + +void InitializeModernUI() +{ + // Create brushes for modern color scheme + hBrushBackground = CreateSolidBrush(COLOR_APP_BACKGROUND); + hBrushSurface = CreateSolidBrush(COLOR_SURFACE); + hBrushPrimary = CreateSolidBrush(COLOR_PRIMARY); + hBrushHover = CreateSolidBrush(COLOR_HOVER); + + // Create modern fonts + hFontRegular = CreateFont(16, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, + DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, + CLEARTYPE_QUALITY, DEFAULT_PITCH | FF_DONTCARE, L"Segoe UI"); + + hFontBold = CreateFont(16, 0, 0, 0, FW_SEMIBOLD, FALSE, FALSE, FALSE, + DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, + CLEARTYPE_QUALITY, DEFAULT_PITCH | FF_DONTCARE, L"Segoe UI"); + + hFontTitle = CreateFont(20, 0, 0, 0, FW_SEMIBOLD, FALSE, FALSE, FALSE, + DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, + CLEARTYPE_QUALITY, DEFAULT_PITCH | FF_DONTCARE, L"Segoe UI"); + + // Create pen for borders + hPenBorder = CreatePen(PS_SOLID, 1, COLOR_BORDER); + + // Initialize GDI+ + Gdiplus::GdiplusStartupInput gdiplusStartupInput; + ULONG_PTR gdiplusToken; + Gdiplus::GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL); +} + +void CleanupModernUI() +{ + // Cleanup GDI objects + if (hBrushBackground) DeleteObject(hBrushBackground); + if (hBrushSurface) DeleteObject(hBrushSurface); + if (hBrushPrimary) DeleteObject(hBrushPrimary); + if (hBrushHover) DeleteObject(hBrushHover); + if (hFontRegular) DeleteObject(hFontRegular); + if (hFontBold) DeleteObject(hFontBold); + if (hFontTitle) DeleteObject(hFontTitle); + if (hPenBorder) DeleteObject(hPenBorder); + + // Shutdown GDI+ + Gdiplus::GdiplusShutdown(NULL); +} + +void DrawModernButton(HDC hdc, RECT rect, const std::wstring& text, bool isHovered, bool isPressed) +{ + // Create rounded rectangle region + HRGN hRgn = CreateRoundRectRgn(rect.left, rect.top, rect.right, rect.bottom, 8, 8); + + // Fill background + HBRUSH hBrush = CreateSolidBrush(isPressed ? COLOR_PRIMARY_DARK : + isHovered ? COLOR_PRIMARY : COLOR_PRIMARY); + FillRgn(hdc, hRgn, hBrush); + DeleteObject(hBrush); + + // Draw border with a brush instead of pen + HBRUSH borderBrush = CreateSolidBrush(COLOR_BORDER); + FrameRgn(hdc, hRgn, borderBrush, 1, 1); + DeleteObject(borderBrush); + DeleteObject(hRgn); + + // Draw text + SetBkMode(hdc, TRANSPARENT); + SetTextColor(hdc, RGB(255, 255, 255)); + SelectObject(hdc, hFontBold); + + DrawText(hdc, text.c_str(), -1, &rect, DT_CENTER | DT_VCENTER | DT_SINGLELINE); +} + +void DrawContactItem(HDC hdc, RECT rect, const Contact& contact, bool isSelected) +{ + // Fill background + HBRUSH bgBrush = CreateSolidBrush(isSelected ? COLOR_HOVER : COLOR_SURFACE); + FillRect(hdc, &rect, bgBrush); + DeleteObject(bgBrush); + + // Draw avatar circle + int avatarX = rect.left + 12; + int avatarY = rect.top + (rect.bottom - rect.top - AVATAR_SIZE) / 2; + + HBRUSH avatarBrush = CreateSolidBrush(COLOR_PRIMARY); + HPEN avatarPen = CreatePen(PS_SOLID, 2, contact.isOnline ? RGB(34, 197, 94) : RGB(156, 163, 175)); + + SelectObject(hdc, avatarBrush); + SelectObject(hdc, avatarPen); + + Ellipse(hdc, avatarX, avatarY, avatarX + AVATAR_SIZE, avatarY + AVATAR_SIZE); + + DeleteObject(avatarBrush); + DeleteObject(avatarPen); + + // Draw contact name + SetBkMode(hdc, TRANSPARENT); + SetTextColor(hdc, COLOR_TEXT_PRIMARY); + SelectObject(hdc, hFontBold); + + RECT nameRect = {avatarX + AVATAR_SIZE + 12, rect.top + 8, rect.right - 8, rect.top + 28}; + DrawText(hdc, contact.name.c_str(), -1, &nameRect, DT_LEFT | DT_VCENTER | DT_SINGLELINE); + + // Draw status + SetTextColor(hdc, COLOR_TEXT_SECONDARY); + SelectObject(hdc, hFontRegular); + + RECT statusRect = {avatarX + AVATAR_SIZE + 12, rect.top + 30, rect.right - 8, rect.top + 48}; + DrawText(hdc, contact.status.c_str(), -1, &statusRect, DT_LEFT | DT_VCENTER | DT_SINGLELINE); + + // Draw last message preview + RECT msgRect = {avatarX + AVATAR_SIZE + 12, rect.top + 50, rect.right - 8, rect.bottom - 8}; + DrawText(hdc, contact.lastMessage.c_str(), -1, &msgRect, DT_LEFT | DT_VCENTER | DT_SINGLELINE | DT_END_ELLIPSIS); +} \ No newline at end of file diff --git a/Samples/ChatAppShare/ShareApp/ModernUI.h b/Samples/ChatAppShare/ShareApp/ModernUI.h new file mode 100644 index 0000000..3cb9ea7 --- /dev/null +++ b/Samples/ChatAppShare/ShareApp/ModernUI.h @@ -0,0 +1,21 @@ +#pragma once + +#include +#include +#include "ChatModels.h" + +// Modern UI Variables +extern HBRUSH hBrushBackground; +extern HBRUSH hBrushSurface; +extern HBRUSH hBrushPrimary; +extern HBRUSH hBrushHover; +extern HFONT hFontRegular; +extern HFONT hFontBold; +extern HFONT hFontTitle; +extern HPEN hPenBorder; + +// Modern UI Functions +void InitializeModernUI(); +void CleanupModernUI(); +void DrawModernButton(HDC hdc, RECT rect, const std::wstring& text, bool isHovered, bool isPressed); +void DrawContactItem(HDC hdc, RECT rect, const Contact& contact, bool isSelected); \ No newline at end of file diff --git a/Samples/ChatAppShare/ShareApp/PackageIdentity.cpp b/Samples/ChatAppShare/ShareApp/PackageIdentity.cpp new file mode 100644 index 0000000..4f4ace9 --- /dev/null +++ b/Samples/ChatAppShare/ShareApp/PackageIdentity.cpp @@ -0,0 +1,463 @@ +#include "framework.h" +#include "PackageIdentity.h" +#include +#include +#include +#include +#include + +// Package Identity Variables +bool g_isSparsePackageSupported = false; +bool g_isRunningWithIdentity = false; +bool g_packageIdentityInitialized = false; + +// Initialize package identity management +bool InitializePackageIdentity() +{ + if (g_packageIdentityInitialized) + return true; + + OutputDebugStringW(L"ChatApp: Initializing package identity management...\n"); + + // Check OS support for sparse packages + g_isSparsePackageSupported = IsSparsePackageSupported(); + + // Check if already running with identity + g_isRunningWithIdentity = IsRunningWithIdentity(); + + g_packageIdentityInitialized = true; + + wchar_t statusLog[256]; + swprintf_s(statusLog, L"ChatApp: Package Identity Status - Sparse supported: %s, Has identity: %s\n", + g_isSparsePackageSupported ? L"Yes" : L"No", + g_isRunningWithIdentity ? L"Yes" : L"No"); + OutputDebugStringW(statusLog); + + return true; +} + +// Register package with external location (improved implementation) +HRESULT RegisterPackageWithExternalLocation(const std::wstring& externalLocation, const std::wstring& packagePath) +{ + try + { + OutputDebugStringW(L"ChatApp: Attempting to register package with external location...\n"); + + wchar_t logBuffer[512]; + swprintf_s(logBuffer, L"ChatApp: External location: %s\n", externalLocation.c_str()); + OutputDebugStringW(logBuffer); + swprintf_s(logBuffer, L"ChatApp: Package path: %s\n", packagePath.c_str()); + OutputDebugStringW(logBuffer); + + // Check if the package file exists + DWORD fileAttributes = GetFileAttributesW(packagePath.c_str()); + if (fileAttributes == INVALID_FILE_ATTRIBUTES) + { + OutputDebugStringW(L"ChatApp: Package file not found, trying PowerShell registration method.\n"); + return RegisterPackageWithExternalLocationPowerShell(externalLocation, packagePath); + } + + // Try PowerShell registration first as it's more reliable + HRESULT powershellResult = RegisterPackageWithExternalLocationPowerShell(externalLocation, packagePath); + if (SUCCEEDED(powershellResult)) + { + OutputDebugStringW(L"ChatApp: Package registration via PowerShell succeeded.\n"); + return powershellResult; + } + + // If PowerShell failed, log the error + wchar_t errorLog[256]; + swprintf_s(errorLog, L"ChatApp: PowerShell registration failed with HRESULT: 0x%08X\n", powershellResult); + OutputDebugStringW(errorLog); + + // For now, return the PowerShell result since we don't have other registration methods implemented + return powershellResult; + } + catch (...) + { + OutputDebugStringW(L"ChatApp: Exception occurred during package registration\n"); + return E_FAIL; + } +} + +// Alternative implementation using PowerShell for package registration +HRESULT RegisterPackageWithExternalLocationPowerShell(const std::wstring& externalLocation, const std::wstring& packagePath) +{ + try + { + OutputDebugStringW(L"ChatApp: Attempting PowerShell package registration...\n"); + + // Check if the package file exists + DWORD fileAttributes = GetFileAttributesW(packagePath.c_str()); + if (fileAttributes == INVALID_FILE_ATTRIBUTES) + { + wchar_t errorLog[512]; + swprintf_s(errorLog, L"ChatApp: Package file not found at: %s\n", packagePath.c_str()); + OutputDebugStringW(errorLog); + return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); + } + + // Build PowerShell command for MSIX package registration with external location + // Use Add-AppxPackage with -ExternalLocation parameter + std::wstring powershellCmd = L"powershell.exe -ExecutionPolicy Bypass -Command \""; + powershellCmd += L"try { "; + powershellCmd += L"Add-AppxPackage -Path '"; + powershellCmd += packagePath; + powershellCmd += L"' -ExternalLocation '"; + powershellCmd += externalLocation; + powershellCmd += L"' -ForceTargetApplicationShutdown; "; + powershellCmd += L"Write-Host 'Package registration successful'; "; + powershellCmd += L"exit 0; "; + powershellCmd += L"} catch { "; + powershellCmd += L"Write-Host ('Package registration failed: ' + $_.Exception.Message); "; + powershellCmd += L"exit 1; "; + powershellCmd += L"}\""; + + wchar_t logBuffer[1024]; + swprintf_s(logBuffer, L"ChatApp: PowerShell command: %s\n", powershellCmd.c_str()); + OutputDebugStringW(logBuffer); + + // Execute PowerShell command + STARTUPINFOW si = {}; + PROCESS_INFORMATION pi = {}; + si.cb = sizeof(si); + si.dwFlags = STARTF_USESHOWWINDOW; + si.wShowWindow = SW_HIDE; + + BOOL success = CreateProcessW( + nullptr, + const_cast(powershellCmd.c_str()), + nullptr, + nullptr, + FALSE, + CREATE_NO_WINDOW, + nullptr, + nullptr, + &si, + &pi + ); + + if (success) + { + OutputDebugStringW(L"ChatApp: PowerShell process started, waiting for completion...\n"); + + // Wait for PowerShell to complete (with timeout) + DWORD waitResult = WaitForSingleObject(pi.hProcess, 60000); // Increased timeout for package registration + + DWORD exitCode = 0; + if (waitResult == WAIT_OBJECT_0) + { + GetExitCodeProcess(pi.hProcess, &exitCode); + wchar_t exitLog[256]; + swprintf_s(exitLog, L"ChatApp: PowerShell process completed with exit code: %d\n", exitCode); + OutputDebugStringW(exitLog); + } + else if (waitResult == WAIT_TIMEOUT) + { + OutputDebugStringW(L"ChatApp: PowerShell process timed out. Package registration may still be in progress.\n"); + TerminateProcess(pi.hProcess, 1); + exitCode = 1; + } + else + { + DWORD waitError = GetLastError(); + wchar_t waitLog[256]; + swprintf_s(waitLog, L"ChatApp: Wait failed with error: %d\n", waitError); + OutputDebugStringW(waitLog); + exitCode = 1; + } + + CloseHandle(pi.hProcess); + CloseHandle(pi.hThread); + + return (exitCode == 0) ? S_OK : E_FAIL; + } + else + { + DWORD error = GetLastError(); + wchar_t errorLog[256]; + swprintf_s(errorLog, L"ChatApp: Failed to start PowerShell process. Error: %d\n", error); + OutputDebugStringW(errorLog); + return HRESULT_FROM_WIN32(error); + } + } + catch (...) + { + OutputDebugStringW(L"ChatApp: Exception occurred during PowerShell package registration\n"); + return E_FAIL; + } +} + +// Relaunch the current application +void RelaunchApplication() +{ + OutputDebugStringW(L"ChatApp: Attempting to relaunch application...\n"); + + wchar_t exePath[MAX_PATH] = {0}; + DWORD len = GetModuleFileNameW(nullptr, exePath, MAX_PATH); + if (len == 0 || len == MAX_PATH) + { + OutputDebugStringW(L"ChatApp: Failed to get executable path for relaunch.\n"); + return; + } + + // Log the executable path + wchar_t logBuffer[512]; + swprintf_s(logBuffer, L"ChatApp: Relaunching: %s\n", exePath); + OutputDebugStringW(logBuffer); + + // Add a small delay to allow package registration to complete + Sleep(2000); + + // Use ShellExecuteW to relaunch the current executable + HINSTANCE result = ShellExecuteW(nullptr, L"open", exePath, nullptr, nullptr, SW_SHOWNORMAL); + + // Fix: Use proper casting for x64 compatibility + INT_PTR resultValue = reinterpret_cast(result); + if (resultValue <= 32) + { + // Log the error + wchar_t errorLog[256]; + swprintf_s(errorLog, L"ChatApp: Failed to relaunch application. ShellExecute error code: %lld\n", + static_cast(resultValue)); + OutputDebugStringW(errorLog); + + // Try alternative relaunch method using CreateProcess + OutputDebugStringW(L"ChatApp: Trying alternative relaunch method...\n"); + + STARTUPINFOW si = {}; + PROCESS_INFORMATION pi = {}; + si.cb = sizeof(si); + si.dwFlags = STARTF_USESHOWWINDOW; + si.wShowWindow = SW_SHOWNORMAL; + + BOOL createResult = CreateProcessW( + exePath, + nullptr, + nullptr, + nullptr, + FALSE, + 0, + nullptr, + nullptr, + &si, + &pi + ); + + if (createResult) + { + OutputDebugStringW(L"ChatApp: Alternative relaunch method succeeded.\n"); + CloseHandle(pi.hProcess); + CloseHandle(pi.hThread); + } + else + { + DWORD createError = GetLastError(); + wchar_t createErrorLog[256]; + swprintf_s(createErrorLog, L"ChatApp: Alternative relaunch also failed. Error: %d\n", createError); + OutputDebugStringW(createErrorLog); + } + } + else + { + OutputDebugStringW(L"ChatApp: Application relaunch initiated successfully.\n"); + } +} + +// Checks if the OS version is Windows 10 2004 (build 19041) or later +bool IsSparsePackageSupported() +{ + // Windows 10 2004 is version 10.0.19041 + OSVERSIONINFOEXW osvi = {}; + osvi.dwOSVersionInfoSize = sizeof(osvi); + + // Get the actual version using RtlGetVersion (undocumented but reliable) + typedef LONG (WINAPI* RtlGetVersionPtr)(PRTL_OSVERSIONINFOEXW); + HMODULE hMod = ::GetModuleHandleW(L"ntdll.dll"); + if (hMod) { + RtlGetVersionPtr fxPtr = (RtlGetVersionPtr)::GetProcAddress(hMod, "RtlGetVersion"); + if (fxPtr != nullptr) { + fxPtr((PRTL_OSVERSIONINFOEXW)&osvi); + + // Log version information for debugging + wchar_t log[256]; + swprintf_s(log, L"ChatApp: Current OS Version: %u.%u.%u\n", + osvi.dwMajorVersion, osvi.dwMinorVersion, osvi.dwBuildNumber); + OutputDebugStringW(log); + } + } + + // Compare with required version (Windows 10 2004 build 19041) + if (osvi.dwMajorVersion > 10 || + (osvi.dwMajorVersion == 10 && osvi.dwMinorVersion > 0) || + (osvi.dwMajorVersion == 10 && osvi.dwMinorVersion == 0 && osvi.dwBuildNumber >= 19041)) + { + OutputDebugStringW(L"ChatApp: Sparse package is supported on this OS.\n"); + return true; + } + + OutputDebugStringW(L"ChatApp: Sparse package is NOT supported on this OS.\n"); + return false; +} + +// Returns true if the app is running with package identity +bool IsRunningWithIdentity() +{ + UINT32 length = 0; + LONG rc = GetCurrentPackageFullName(&length, nullptr); + + if (rc == ERROR_INSUFFICIENT_BUFFER) + { + std::vector packageFullName(length); + rc = GetCurrentPackageFullName(&length, packageFullName.data()); + if (rc == ERROR_SUCCESS) + { + OutputDebugStringW(L"ChatApp: Running with package identity.\n"); + return true; + } + } + + OutputDebugStringW(L"ChatApp: Not running with package identity.\n"); + return false; +} + +// Helper to get the directory of the current executable +std::wstring GetExecutableDirectory() +{ + wchar_t exePath[MAX_PATH] = {0}; + DWORD len = GetModuleFileNameW(nullptr, exePath, MAX_PATH); + if (len == 0 || len == MAX_PATH) + { + OutputDebugStringW(L"ChatApp: Failed to get executable path.\n"); + return L""; + } + + std::wstring path(exePath); + size_t pos = path.find_last_of(L"\\/"); + if (pos != std::wstring::npos) + path = path.substr(0, pos); + + return path; +} + +// Initialize package identity-specific flows and features +void InitializePackageIdentityFlow() +{ + OutputDebugStringW(L"ChatApp: Initializing package identity-specific flows...\n"); + + if (!g_isRunningWithIdentity) { + OutputDebugStringW(L"ChatApp: Not running with package identity - skipping identity-specific flows.\n"); + return; + } + + // TODO: Add package identity-specific initialization here: + + // 1. Single Instance Management + // - Check for existing app instances + // - Register current instance + // - Handle instance redirection + + // 2. Share Target Registration + // - Check for share target activation + // - Initialize share target handlers + // - Set up cross-process communication + + // 3. Enhanced Security Features + // - Initialize secure file handling + // - Set up identity-based permissions + // - Enable enhanced data protection + + // 4. App Model Integration + // - Register activation handlers + // - Set up background task support + // - Initialize notification system + + OutputDebugStringW(L"ChatApp: Package identity flows initialized (placeholder - features to be implemented).\n"); +} + +// Helper to validate MSIX package existence and basic properties +bool ValidateMsixPackage(const std::wstring& packagePath) +{ + OutputDebugStringW(L"ChatApp: Validating MSIX package...\n"); + + // Check if file exists + DWORD fileAttributes = GetFileAttributesW(packagePath.c_str()); + if (fileAttributes == INVALID_FILE_ATTRIBUTES) + { + wchar_t errorLog[512]; + swprintf_s(errorLog, L"ChatApp: MSIX package not found at: %s\n", packagePath.c_str()); + OutputDebugStringW(errorLog); + return false; + } + + // Check if it's a file (not a directory) + if (fileAttributes & FILE_ATTRIBUTE_DIRECTORY) + { + OutputDebugStringW(L"ChatApp: Package path points to a directory, not a file.\n"); + return false; + } + + // Check file extension + size_t dotPos = packagePath.find_last_of(L'.'); + if (dotPos == std::wstring::npos) + { + OutputDebugStringW(L"ChatApp: Package file has no extension.\n"); + return false; + } + + std::wstring extension = packagePath.substr(dotPos); + std::transform(extension.begin(), extension.end(), extension.begin(), ::towlower); + + if (extension != L".msix" && extension != L".appx") + { + wchar_t extLog[256]; + swprintf_s(extLog, L"ChatApp: Package has unexpected extension: %s\n", extension.c_str()); + OutputDebugStringW(extLog); + return false; + } + + // Get file size + HANDLE hFile = CreateFileW(packagePath.c_str(), GENERIC_READ, FILE_SHARE_READ, + nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr); + if (hFile != INVALID_HANDLE_VALUE) + { + LARGE_INTEGER fileSize; + if (GetFileSizeEx(hFile, &fileSize)) + { + wchar_t sizeLog[256]; + swprintf_s(sizeLog, L"ChatApp: Package size: %lld bytes\n", fileSize.QuadPart); + OutputDebugStringW(sizeLog); + } + CloseHandle(hFile); + } + + OutputDebugStringW(L"ChatApp: MSIX package validation passed.\n"); + return true; +} + +// Get current package identity status as a formatted string +std::wstring GetPackageIdentityStatus() +{ + std::wstring status = L"ChatApp Package Identity Status:\n"; + status += L"- Sparse package supported: " + std::wstring(g_isSparsePackageSupported ? L"Yes" : L"No") + L"\n"; + status += L"- Running with identity: " + std::wstring(g_isRunningWithIdentity ? L"Yes" : L"No") + L"\n"; + status += L"- Initialized: " + std::wstring(g_packageIdentityInitialized ? L"Yes" : L"No") + L"\n"; + + if (g_isRunningWithIdentity) + { + // Try to get package full name + UINT32 length = 0; + LONG rc = GetCurrentPackageFullName(&length, nullptr); + if (rc == ERROR_INSUFFICIENT_BUFFER && length > 0) + { + std::vector packageFullName(length); + rc = GetCurrentPackageFullName(&length, packageFullName.data()); + if (rc == ERROR_SUCCESS) + { + status += L"- Package full name: " + std::wstring(packageFullName.data()) + L"\n"; + } + } + } + + return status; +} \ No newline at end of file diff --git a/Samples/ChatAppShare/ShareApp/PackageIdentity.h b/Samples/ChatAppShare/ShareApp/PackageIdentity.h new file mode 100644 index 0000000..e97fa85 --- /dev/null +++ b/Samples/ChatAppShare/ShareApp/PackageIdentity.h @@ -0,0 +1,23 @@ +#pragma once + +#include +#include + +// Package Identity Variables +extern bool g_isSparsePackageSupported; +extern bool g_isRunningWithIdentity; +extern bool g_packageIdentityInitialized; + +// Package Identity Management functions +bool IsSparsePackageSupported(); +bool IsRunningWithIdentity(); +bool InitializePackageIdentity(); +void InitializePackageIdentityFlow(); +std::wstring GetExecutableDirectory(); +HRESULT RegisterPackageWithExternalLocation(const std::wstring& externalLocation, const std::wstring& packagePath); +HRESULT RegisterPackageWithExternalLocationPowerShell(const std::wstring& externalLocation, const std::wstring& packagePath); +void RelaunchApplication(); + +// Helper functions +bool ValidateMsixPackage(const std::wstring& packagePath); +std::wstring GetPackageIdentityStatus(); \ No newline at end of file diff --git a/Samples/ChatAppShare/ShareApp/Resource.h b/Samples/ChatAppShare/ShareApp/Resource.h new file mode 100644 index 0000000..e41d7d3 --- /dev/null +++ b/Samples/ChatAppShare/ShareApp/Resource.h @@ -0,0 +1,48 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by SampleChatAppWithShare.rc + +#define IDS_APP_TITLE 103 + +#define IDR_MAINFRAME 128 +#define IDD_SAMPLECHATAPPWITHSHARE_DIALOG 102 +#define IDD_ABOUTBOX 103 +#define IDD_CONTACT_SELECTION 104 +#define IDM_ABOUT 105 +#define IDM_EXIT 106 +#define IDI_SAMPLECHATAPPWITHSHARE 107 +#define IDI_SMALL 108 +#define IDC_SAMPLECHATAPPWITHSHARE 109 +#define IDC_MYICON 2 + +// Chat Application Controls +#define IDC_CONTACTS_LIST 1001 +#define IDC_CHAT_DISPLAY 1002 +#define IDC_MESSAGE_INPUT 1003 +#define IDC_SEND_BUTTON 1004 +#define IDC_CONTACT_NAME 1005 +#define IDC_SHARE_FILE_BUTTON 1006 +#define IDC_SHARED_FILES_LIST 1007 + +// Contact Selection Dialog Controls +#define IDC_CONTACT_SELECTION_LIST 1008 +#define IDC_SELECT_CONTACT_BUTTON 1009 +#define IDC_CANCEL_SELECTION_BUTTON 1010 +#define IDC_SHARE_MESSAGE_EDIT 1011 +#define IDC_DIALOG_TITLE 1012 + +#ifndef IDC_STATIC +#define IDC_STATIC -1 +#endif +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS + +#define _APS_NO_MFC 130 +#define _APS_NEXT_RESOURCE_VALUE 129 +#define _APS_NEXT_COMMAND_VALUE 32771 +#define _APS_NEXT_CONTROL_VALUE 1013 +#define _APS_NEXT_SYMED_VALUE 110 +#endif +#endif diff --git a/Samples/ChatAppShare/ShareApp/ShareApp.cpp b/Samples/ChatAppShare/ShareApp/ShareApp.cpp new file mode 100644 index 0000000..9a916e2 --- /dev/null +++ b/Samples/ChatAppShare/ShareApp/ShareApp.cpp @@ -0,0 +1,457 @@ +// SampleChatAppWithShare.cpp : Defines the entry point for the application. +// + +#include "framework.h" +#include "ShareApp.h" +#include "UIConstants.h" +#include "ChatModels.h" +#include "ModernUI.h" +#include "ChatManager.h" +#include "FileManager.h" +#include "UIManager.h" +#include "PackageIdentity.h" +#include "ShareTargetManager.h" +#include "WindowProcs.h" +#include +#include +#include +#include +#include + +#pragma comment(lib, "comctl32.lib") +#pragma comment(lib, "shell32.lib") +#pragma comment(lib, "uxtheme.lib") +#pragma comment(lib, "ole32.lib") +// Add WinRT libraries to fix linking error +#pragma comment(lib, "windowsapp.lib") +#pragma comment(lib, "runtimeobject.lib") + +using namespace winrt; +using namespace Windows::Foundation; +using namespace Windows::ApplicationModel; +using namespace Windows::ApplicationModel::Activation; +using namespace Windows::ApplicationModel::DataTransfer; +using namespace Windows::ApplicationModel::DataTransfer::ShareTarget; + +// Global Variables: +HINSTANCE hInst; // current instance +WCHAR szTitle[MAX_LOADSTRING]; // The title bar text +WCHAR szWindowClass[MAX_LOADSTRING]; // the main window class name + +// Global flag to track if we're in share-only mode +bool g_isShareOnlyMode = false; + +// Forward declarations of functions included in this code module: +ATOM MyRegisterClass(HINSTANCE hInstance); +BOOL InitInstance(HINSTANCE, int); +LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); +INT_PTR CALLBACK About(HWND, UINT, WPARAM, LPARAM); + +int APIENTRY wWinMain(_In_ HINSTANCE hInstance, + _In_opt_ HINSTANCE hPrevInstance, + _In_ LPWSTR lpCmdLine, + _In_ int nCmdShow) +{ + UNREFERENCED_PARAMETER(hPrevInstance); + UNREFERENCED_PARAMETER(lpCmdLine); + + // Init package identity checks + InitializePackageIdentity(); + + // Initialize WinRT with proper error handling + try + { + winrt::init_apartment(); + OutputDebugStringW(L"ChatApp: WinRT initialized successfully.\n"); + } + catch (hresult_error const& ex) + { + std::wstring errorMsg = L"ChatApp: WinRT initialization failed: " + std::wstring(ex.message().c_str()) + + L" (HRESULT: 0x" + std::to_wstring(static_cast(ex.code())) + L")\n"; + OutputDebugStringW(errorMsg.c_str()); + } + catch (...) + { + OutputDebugStringW(L"ChatApp: WinRT initialization failed with unknown error.\n"); + } + + // Initialize Share Target Manager + ShareTargetManager::Initialize(); + + // Note: Don't process share target activation here - UI isn't created yet + // This will be handled after UI creation in WndProc WM_CREATE + + // Check if this is a share target activation to determine if we need the main window + if (g_isRunningWithIdentity && ShareTargetManager::IsShareTargetActivation()) + { + g_isShareOnlyMode = true; + OutputDebugStringW(L"ChatApp: Running in share-only mode - main window will not be created\n"); + } + + if (g_isSparsePackageSupported && !g_isRunningWithIdentity) + { + std::wstring executableDir = GetExecutableDirectory(); + std::wstring packagePath = executableDir + L"\\Weixin_1.0.1.0_x86__v4k3sbdawh17a.msix"; + + // Validate the MSIX package before attempting registration + if (ValidateMsixPackage(packagePath)) + { + HRESULT result = RegisterPackageWithExternalLocation(executableDir, packagePath); + if (SUCCEEDED(result)) + { + OutputDebugStringW(L"ChatApp: Package registration succeeded. Relaunching...\n"); + RelaunchApplication(); + return 0; // Exit after relaunch + } + else + { + // Log the error but continue without package identity + wchar_t errorLog[256]; + swprintf_s(errorLog, L"ChatApp: Failed to register package. HRESULT: 0x%08X. Continuing without package identity.\n", result); + OutputDebugStringW(errorLog); + } + } + else + { + OutputDebugStringW(L"ChatApp: MSIX package validation failed. Continuing without package identity.\n"); + } + } + else if (g_isRunningWithIdentity) + { + // Process share target activation using the ShareTargetManager + ShareTargetManager::ProcessActivationArgs(); + } + else + { + // Log the current status + std::wstring status = GetPackageIdentityStatus(); + OutputDebugStringW(status.c_str()); + } + + // Initialize COM for shell operations + CoInitialize(NULL); + + // Initialize common controls + INITCOMMONCONTROLSEX icex; + icex.dwSize = sizeof(INITCOMMONCONTROLSEX); + icex.dwICC = ICC_LISTVIEW_CLASSES | ICC_STANDARD_CLASSES; + InitCommonControlsEx(&icex); + + // Initialize global strings + LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING); + LoadStringW(hInstance, IDC_SAMPLECHATAPPWITHSHARE, szWindowClass, MAX_LOADSTRING); + + if (!g_isShareOnlyMode) + { + // Only register window class if we're not in share-only mode + MyRegisterClass(hInstance); + + // Initialize modern UI + InitializeModernUI(); + } + + // Initialize dummy contacts (needed for share target) + InitializeContacts(); + + if (g_isShareOnlyMode) + { + // In share-only mode, process the share target directly without creating a window + OutputDebugStringW(L"ChatApp: Processing share target in share-only mode\n"); + ShareTargetManager::ProcessActivationArgs(); + } + else + { + // Normal mode: create and show the main window + if (!InitInstance(hInstance, nCmdShow)) + { + return FALSE; + } + } + + HACCEL hAccelTable = nullptr; + if (!g_isShareOnlyMode) + { + hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_SAMPLECHATAPPWITHSHARE)); + } + + MSG msg; + + // Main message loop: + while (GetMessage(&msg, nullptr, 0, 0)) + { + if (!g_isShareOnlyMode && !TranslateAccelerator(msg.hwnd, hAccelTable, &msg)) + { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + else if (g_isShareOnlyMode) + { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + } + + CoUninitialize(); + return (int) msg.wParam; +} + +// +// FUNCTION: MyRegisterClass() +// +// PURPOSE: Registers the window class. +// +ATOM MyRegisterClass(HINSTANCE hInstance) +{ + WNDCLASSEXW wcex; + + wcex.cbSize = sizeof(WNDCLASSEX); + + wcex.style = CS_HREDRAW | CS_VREDRAW; + wcex.lpfnWndProc = WndProc; + wcex.cbClsExtra = 0; + wcex.cbWndExtra = 0; + wcex.hInstance = hInstance; + wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_SAMPLECHATAPPWITHSHARE)); + wcex.hCursor = LoadCursor(nullptr, IDC_ARROW); + wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); + wcex.lpszMenuName = MAKEINTRESOURCEW(IDC_SAMPLECHATAPPWITHSHARE); + wcex.lpszClassName = szWindowClass; + wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL)); + + return RegisterClassExW(&wcex); +} + +// +// FUNCTION: InitInstance(HINSTANCE, int) +// +// PURPOSE: Saves instance handle and creates main window +// +// COMMENTS: +// +// In this function, we save the instance handle in a global variable and +// create and display the main program window. +// +BOOL InitInstance(HINSTANCE hInstance, int nCmdShow) +{ + hInst = hInstance; // Store instance handle in our global variable + + HWND hWnd = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW, + CW_USEDEFAULT, 0, 1200, 700, nullptr, nullptr, hInstance, nullptr); + + if (!hWnd) + { + return FALSE; + } + + ShowWindow(hWnd, nCmdShow); + UpdateWindow(hWnd); + + return TRUE; +} + +// +// FUNCTION: WndProc(HWND, UINT, WPARAM, LPARAM) +// +// PURPOSE: Processes messages for the main window. +// +LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + switch (message) + { + case WM_CREATE: + CreateChatUI(hWnd); + + // Process share target activation after UI is created + if (g_isRunningWithIdentity) + { + ShareTargetManager::ProcessActivationArgs(); + } + break; + + case WM_SIZE: + ResizeChatUI(hWnd); + break; + + case WM_CTLCOLORSTATIC: + { + HDC hdcStatic = (HDC)wParam; + SetTextColor(hdcStatic, COLOR_TEXT_PRIMARY); + SetBkColor(hdcStatic, COLOR_APP_BACKGROUND); + return (INT_PTR)hBrushBackground; + } + + case WM_CTLCOLOREDIT: + { + HDC hdcEdit = (HDC)wParam; + SetTextColor(hdcEdit, COLOR_TEXT_PRIMARY); + SetBkColor(hdcEdit, COLOR_SURFACE); + return (INT_PTR)hBrushSurface; + } + + case WM_CTLCOLORLISTBOX: + { + HDC hdcList = (HDC)wParam; + SetTextColor(hdcList, COLOR_TEXT_PRIMARY); + SetBkColor(hdcList, COLOR_SURFACE); + return (INT_PTR)hBrushSurface; + } + + case WM_PAINT: + { + PAINTSTRUCT ps; + HDC hdc = BeginPaint(hWnd, &ps); + + // Fill the main window background with modern color + RECT rect; + GetClientRect(hWnd, &rect); + FillRect(hdc, &rect, hBrushBackground); + + EndPaint(hWnd, &ps); + } + break; + + case WM_COMMAND: + { + int wmId = LOWORD(wParam); + int wmEvent = HIWORD(wParam); + + // Parse the menu selections: + switch (wmId) + { + case IDM_ABOUT: + DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About); + break; + case IDM_EXIT: + DestroyWindow(hWnd); + break; + case IDC_CONTACTS_LIST: + if (wmEvent == LBN_SELCHANGE) { + int selectedIndex = (int)::SendMessage(hContactsList, LB_GETCURSEL, 0, 0); + LoadContactChat(selectedIndex); + UpdateSharedFilesList(); + } + break; + case IDC_SEND_BUTTON: + SendChatMessage(); + break; + case IDC_SHARE_FILE_BUTTON: + ShareFile(); + break; + case IDC_SHARED_FILES_LIST: + if (wmEvent == LBN_DBLCLK) { + int selectedFile = (int)::SendMessage(hSharedFilesList, LB_GETCURSEL, 0, 0); + OpenSharedFile(selectedFile); + } + break; + case IDC_MESSAGE_INPUT: + if (wmEvent == EN_CHANGE) { + // Enable/disable send button based on input with visual feedback + WCHAR buffer[1024]; + GetWindowText(hMessageInput, buffer, 1024); + bool hasText = wcslen(buffer) > 0; + EnableWindow(hSendButton, hasText); + InvalidateRect(hSendButton, NULL, FALSE); + } + break; + case 2000: // Test WinRT Share Target Status + { + // Use ShareTargetManager to get status + std::wstring statusInfo = ShareTargetManager::GetShareTargetStatus(); + MessageBoxW(hWnd, statusInfo.c_str(), L"WinRT Share Target Status", MB_OK | MB_ICONINFORMATION); + } + break; + default: + return DefWindowProc(hWnd, message, wParam, lParam); + } + } + break; + + case WM_TIMER: + ProcessAutoReply(hWnd, (int)wParam); + break; + + case WM_KEYDOWN: + { + if (GetFocus() == hMessageInput && wParam == VK_RETURN) { + if (!(GetKeyState(VK_SHIFT) & 0x8000)) { + // Enter without Shift sends the message + SendChatMessage(); + return 0; + } + } + } + break; + + case WM_DESTROY: + CleanupModernUI(); + PostQuitMessage(0); + break; + + default: + return DefWindowProc(hWnd, message, wParam, lParam); + } + return 0; +} + +// Message handler for about box. +INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) +{ + UNREFERENCED_PARAMETER(lParam); + switch (message) + { + case WM_INITDIALOG: + { + // Add WinRT and package identity information to the about dialog + std::wstring aboutText = L"Chat Application with WinRT Share Target Support\n\n"; + + // Test WinRT status + try + { + winrt::check_hresult(S_OK); + aboutText += L"? WinRT Status: ? Initialized and Working\n"; + + if (g_isRunningWithIdentity) + { + if (ShareTargetManager::IsShareTargetActivation()) + { + aboutText += L"?? Share Target: ? Currently Activated via Windows Share Sheet\n"; + } + else + { + aboutText += L"?? Share Target: ? Ready for Activation\n"; + } + } + else + { + aboutText += L"?? Share Target: ? Package Identity Required\n"; + } + } + catch (...) + { + aboutText += L"?? WinRT Status: ? Error or Not Available\n"; + } + + aboutText += L"?? Package Identity: " + std::wstring(g_isRunningWithIdentity ? L"? Available" : L"? Not Available") + L"\n"; + aboutText += L"?? Sparse Package Support: " + std::wstring(g_isSparsePackageSupported ? L"? Supported" : L"? Not Supported") + L"\n\n"; + aboutText += L"Current Features:\n"; + aboutText += L"? WinRT Runtime Integration\n"; + aboutText += L"? Windows Share Target Support\n"; + aboutText += L"? Package Identity Management\n"; + aboutText += L"? MSIX Package Registration\n"; + aboutText += L"? Modern Chat UI\n\n"; + aboutText += GetPackageIdentityStatus(); + + SetDlgItemTextW(hDlg, IDC_STATIC, aboutText.c_str()); + return (INT_PTR)TRUE; + } + + case WM_COMMAND: + if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL) + { + EndDialog(hDlg, LOWORD(wParam)); + return (INT_PTR)TRUE; + } + break; + } + return (INT_PTR)FALSE; +} diff --git a/Samples/ChatAppShare/ShareApp/ShareApp.exe.manifest b/Samples/ChatAppShare/ShareApp/ShareApp.exe.manifest new file mode 100644 index 0000000..92ae86a --- /dev/null +++ b/Samples/ChatAppShare/ShareApp/ShareApp.exe.manifest @@ -0,0 +1,9 @@ + + + + + diff --git a/Samples/ChatAppShare/ShareApp/ShareApp.h b/Samples/ChatAppShare/ShareApp/ShareApp.h new file mode 100644 index 0000000..d00d47e --- /dev/null +++ b/Samples/ChatAppShare/ShareApp/ShareApp.h @@ -0,0 +1,3 @@ +#pragma once + +#include "resource.h" diff --git a/Samples/ChatAppShare/ShareApp/ShareApp.ico b/Samples/ChatAppShare/ShareApp/ShareApp.ico new file mode 100644 index 0000000000000000000000000000000000000000..b3ec03bd617f32e58128fa977fd6ac9605124f4b GIT binary patch literal 46227 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 UsS9!GIA?o=m~su~eMXaE2J literal 0 HcmV?d00001 diff --git a/Samples/ChatAppShare/ShareApp/ShareApp.vcxproj b/Samples/ChatAppShare/ShareApp/ShareApp.vcxproj new file mode 100644 index 0000000..9aab09b --- /dev/null +++ b/Samples/ChatAppShare/ShareApp/ShareApp.vcxproj @@ -0,0 +1,234 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + Debug + ARM64 + + + Release + ARM64 + + + + 17.0 + Win32Proj + {006AFCE4-5393-4696-9319-9FDD3054774E} + ShareApp + 10.0 + + + + Application + true + v143 + Unicode + + + Application + false + v143 + true + Unicode + + + Application + true + v143 + Unicode + + + Application + false + v143 + true + Unicode + + + Application + true + v143 + Unicode + + + Application + false + v143 + true + Unicode + + + + + + + + + + + + + + + + + + + + + + + + + + + + Level3 + true + WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions) + true + stdcpp17 + + + Windows + true + + + + + Level3 + true + true + true + WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions) + true + stdcpp17 + + + Windows + true + + + + + Level3 + true + _DEBUG;_WINDOWS;%(PreprocessorDefinitions) + true + stdcpp17 + + + Windows + true + + + + + Level3 + true + true + true + NDEBUG;_WINDOWS;%(PreprocessorDefinitions) + true + stdcpp17 + + + Windows + true + + + + + Level3 + true + _DEBUG;_WINDOWS;%(PreprocessorDefinitions) + true + stdcpp17 + + + Windows + true + + + + + Level3 + true + true + true + NDEBUG;_WINDOWS;%(PreprocessorDefinitions) + true + stdcpp17 + + + Windows + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Samples/ChatAppShare/ShareApp/ShareApp.vcxproj.filters b/Samples/ChatAppShare/ShareApp/ShareApp.vcxproj.filters new file mode 100644 index 0000000..8a47ef7 --- /dev/null +++ b/Samples/ChatAppShare/ShareApp/ShareApp.vcxproj.filters @@ -0,0 +1,118 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Resource Files + + + + + Resource Files + + + Resource Files + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Samples/ChatAppShare/ShareApp/ShareTargetManager.cpp b/Samples/ChatAppShare/ShareApp/ShareTargetManager.cpp new file mode 100644 index 0000000..82131dc --- /dev/null +++ b/Samples/ChatAppShare/ShareApp/ShareTargetManager.cpp @@ -0,0 +1,425 @@ +#include "ShareTargetManager.h" +#include "PackageIdentity.h" +#include "ContactSelectionDialog.h" +#include "ChatManager.h" +#include "ChatModels.h" +#include "FileManager.h" +#include "framework.h" +#include + +// Static member initialization +bool ShareTargetManager::s_initialized = false; +bool ShareTargetManager::s_shareTargetSupported = false; + +void ShareTargetManager::Initialize() +{ + if (s_initialized) + return; + + LogShareInfo(L"Initializing Share Target Manager..."); + + try + { + // Check if WinRT and package identity are available + if (g_isRunningWithIdentity) + { + s_shareTargetSupported = true; + LogShareInfo(L"Share Target support available with package identity."); + } + else + { + s_shareTargetSupported = false; + LogShareInfo(L"Share Target requires package identity (not available)."); + } + } + catch (winrt::hresult_error const& ex) + { + std::wstring error = L"Share Target initialization error: " + std::wstring(ex.message().c_str()); + LogShareError(error); + s_shareTargetSupported = false; + } + catch (...) + { + LogShareError(L"Unknown error during Share Target initialization."); + s_shareTargetSupported = false; + } + + s_initialized = true; + LogShareInfo(L"Share Target Manager initialized successfully."); +} + +bool ShareTargetManager::IsShareTargetAvailable() +{ + if (!s_initialized) + Initialize(); + + return s_shareTargetSupported && g_isRunningWithIdentity; +} + +bool ShareTargetManager::IsShareTargetActivation() +{ + if (!IsShareTargetAvailable()) + return false; + + try + { + auto activationArgs = winrt::Windows::ApplicationModel::AppInstance::GetActivatedEventArgs(); + return activationArgs && activationArgs.Kind() == winrt::Windows::ApplicationModel::Activation::ActivationKind::ShareTarget; + } + catch (...) + { + LogShareError(L"Error checking share target activation."); + return false; + } +} + +bool ShareTargetManager::ProcessActivationArgs() +{ + if (!IsShareTargetAvailable()) + return false; + + // Add a static flag to prevent multiple processing + static bool s_alreadyProcessed = false; + if (s_alreadyProcessed) { + LogShareInfo(L"Share target already processed - skipping duplicate call"); + return false; + } + + try + { + auto activationArgs = winrt::Windows::ApplicationModel::AppInstance::GetActivatedEventArgs(); + if (activationArgs && activationArgs.Kind() == winrt::Windows::ApplicationModel::Activation::ActivationKind::ShareTarget) + { + LogShareInfo(L"? Share Target activation detected!"); + s_alreadyProcessed = true; // Mark as processed + + // Ensure contacts are initialized (critical for share target scenarios) + if (contacts.empty()) + { + LogShareInfo(L"Initializing contacts for share target scenario..."); + InitializeContacts(); + } + + // Process the share target directly here + auto shareArgs = activationArgs.as(); + auto shareOperation = shareArgs.ShareOperation(); + auto data = shareOperation.Data(); + + // Extract shared content information for the contact selection dialog + std::wstring sharedContentSummary = L"Shared Content"; + std::wstring sharedFiles; + bool hasFiles = false; + + // Collect information about what's being shared + std::vector sharedItems; + + // Check for different data formats + if (data.Contains(winrt::Windows::ApplicationModel::DataTransfer::StandardDataFormats::Text())) + { + try + { + auto textAsync = data.GetTextAsync(); + auto text = textAsync.get(); + sharedItems.push_back(L"Text: " + std::wstring(text.c_str())); + LogShareInfo(L"Received shared text."); + } + catch (...) + { + sharedItems.push_back(L"Text: [Error retrieving text]"); + LogShareError(L"Error retrieving shared text."); + } + } + + if (data.Contains(winrt::Windows::ApplicationModel::DataTransfer::StandardDataFormats::WebLink())) + { + try + { + auto webLinkAsync = data.GetWebLinkAsync(); + auto webLink = webLinkAsync.get(); + sharedItems.push_back(L"Web Link: " + std::wstring(webLink.ToString().c_str())); + LogShareInfo(L"Received shared web link."); + } + catch (...) + { + sharedItems.push_back(L"Web Link: [Error retrieving web link]"); + LogShareError(L"Error retrieving shared web link."); + } + } + + if (data.Contains(winrt::Windows::ApplicationModel::DataTransfer::StandardDataFormats::Bitmap())) + { + try + { + auto bitmapAsync = data.GetBitmapAsync(); + auto bitmapRef = bitmapAsync.get(); + sharedItems.push_back(L"Image/Bitmap content"); + LogShareInfo(L"Received shared bitmap."); + } + catch (...) + { + sharedItems.push_back(L"Image: [Error retrieving image]"); + LogShareError(L"Error retrieving shared bitmap."); + } + } + + if (data.Contains(winrt::Windows::ApplicationModel::DataTransfer::StandardDataFormats::StorageItems())) + { + try + { + auto storageItemsAsync = data.GetStorageItemsAsync(); + auto storageItems = storageItemsAsync.get(); + + hasFiles = true; + std::wstring filesInfo = std::to_wstring(storageItems.Size()) + L" file(s)"; + + if (storageItems.Size() == 1) + { + auto item = storageItems.GetAt(0); + sharedFiles = item.Name().c_str(); + sharedContentSummary = sharedFiles; + } + else if (storageItems.Size() > 1) + { + sharedFiles = L"Multiple Files (" + std::to_wstring(storageItems.Size()) + L")"; + sharedContentSummary = sharedFiles; + } + + for (uint32_t i = 0; i < storageItems.Size(); i++) + { + auto item = storageItems.GetAt(i); + filesInfo += L"\n - " + std::wstring(item.Name().c_str()); + } + + sharedItems.push_back(filesInfo); + LogShareInfo(L"Received shared files."); + } + catch (...) + { + sharedItems.push_back(L"Files: [Error retrieving files]"); + LogShareError(L"Error retrieving shared files."); + } + } + + // If no specific content type found, use generic description + if (sharedItems.empty()) + { + sharedContentSummary = L"Shared Content"; + sharedItems.push_back(L"Unknown content type"); + } + + // Get the main window handle for dialog parent + HWND hMainWindow = GetActiveWindow(); + if (!hMainWindow) + { + hMainWindow = GetForegroundWindow(); + } + if (!hMainWindow) + { + // Try to find the main chat application window + hMainWindow = FindWindow(NULL, L"Chat Application"); + } + + // Log the number of contacts available for debugging + LogShareInfo(L"Available contacts: " + std::to_wstring(contacts.size())); + + // Show contact selection dialog for the shared content + ContactSelectionDialog::SelectionResult result = + ContactSelectionDialog::ShowContactSelectionDialog(hMainWindow, L"", sharedContentSummary); + + if (result.wasSelected) + { + // User selected a contact - add the shared content to that contact's chat + if (result.contactIndex >= 0 && result.contactIndex < (int)contacts.size()) + { + int previousSelection = selectedContactIndex; + selectedContactIndex = result.contactIndex; + + LogShareInfo(L"Setting selectedContactIndex to: " + std::to_wstring(result.contactIndex)); + LogShareInfo(L"Selected contact name: " + contacts[result.contactIndex].name); + + // Add messages directly to the selected contact instead of relying on selectedContactIndex + Contact& selectedContact = contacts[result.contactIndex]; + + // Add the custom share message if provided + if (!result.shareMessage.empty()) + { + std::wstring formattedShareMessage = L"You: " + result.shareMessage + L" ??"; + selectedContact.messages.push_back(formattedShareMessage); + LogShareInfo(L"Added share message: " + result.shareMessage); + } + + // Add shared content messages to the chat + for (const auto& item : sharedItems) + { + std::wstring shareMsg = L"?? Received via Share: " + item; + std::wstring formattedMessage = selectedContact.name + L": " + shareMsg; + selectedContact.messages.push_back(formattedMessage); + LogShareInfo(L"Added shared content message: " + shareMsg); + } + + // Update the last message preview for this contact + if (!sharedItems.empty()) + { + std::wstring lastMsg = L"?? Received via Share: " + sharedItems[0]; + selectedContact.lastMessage = lastMsg.length() > 50 ? lastMsg.substr(0, 47) + L"..." : lastMsg; + } + + // If files were shared, add them to the contact's shared files list + if (hasFiles && data.Contains(winrt::Windows::ApplicationModel::DataTransfer::StandardDataFormats::StorageItems())) + { + try + { + auto storageItemsAsync = data.GetStorageItemsAsync(); + auto storageItems = storageItemsAsync.get(); + + for (uint32_t i = 0; i < storageItems.Size(); i++) + { + auto item = storageItems.GetAt(i); + + // Create shared file entry + SharedFile newFile; + newFile.fileName = item.Name().c_str(); + newFile.filePath = item.Path().c_str(); // Get full path if available + newFile.sharedBy = L"External Share"; + GetSystemTime(&newFile.timeShared); + + selectedContact.sharedFiles.push_back(newFile); + LogShareInfo(L"Added shared file: " + newFile.fileName); + } + } + catch (...) + { + LogShareError(L"Error adding shared files to contact."); + } + } + + // Show success message and exit the application + std::wstring successMsg = L"Content has been shared successfully with " + contacts[result.contactIndex].name + L"!\n\nThe application will now close."; + MessageBoxW(hMainWindow, successMsg.c_str(), L"Sharing Complete", MB_OK | MB_ICONINFORMATION); + + LogShareInfo(L"Share target content added to contact: " + contacts[result.contactIndex].name); + LogShareInfo(L"Selected contact index set to: " + std::to_wstring(result.contactIndex)); + LogShareInfo(L"Contact now has " + std::to_wstring(selectedContact.messages.size()) + L" messages"); + + // Report completion to Windows + shareOperation.ReportCompleted(); + + LogShareInfo(L"Share target processing completed successfully. Exiting application."); + + // Exit the application after successful sharing + PostQuitMessage(0); + return true; + } + } + else + { + // User cancelled - show a brief message and exit + MessageBoxW(hMainWindow, L"Share operation was cancelled.\n\nThe application will now close.", L"Share Cancelled", MB_OK | MB_ICONINFORMATION); + LogShareInfo(L"Share target operation cancelled by user."); + + // Report completion to Windows + shareOperation.ReportCompleted(); + + LogShareInfo(L"Share target processing cancelled. Exiting application."); + + // Exit the application after cancellation + PostQuitMessage(0); + return true; + } + + // Report completion to Windows + shareOperation.ReportCompleted(); + + LogShareInfo(L"Share target processing completed successfully."); + return true; + } + else + { + LogShareInfo(L"Running with package identity but not as share target."); + return false; + } + } + catch (winrt::hresult_error const& ex) + { + s_alreadyProcessed = true; // Mark as processed even on error to prevent retries + std::wstring error = L"Error checking activation args: " + std::wstring(ex.message().c_str()) + + L" (HRESULT: 0x" + std::to_wstring(static_cast(ex.code())) + L")"; + LogShareError(error); + MessageBoxW(nullptr, L"Error processing shared content", L"Share Target Error", MB_OK | MB_ICONERROR); + return false; + } + catch (...) + { + s_alreadyProcessed = true; // Mark as processed even on error to prevent retries + LogShareError(L"Unknown error checking activation arguments."); + MessageBoxW(nullptr, L"Unknown error processing shared content", L"Share Target Error", MB_OK | MB_ICONERROR); + return false; + } +} + +std::wstring ShareTargetManager::GetShareTargetStatus() +{ + if (!s_initialized) + Initialize(); + + std::wstring status = L"WinRT Share Target Integration Status:\n\n"; + + try + { + winrt::check_hresult(S_OK); + status += L"?? WinRT Runtime: ? Available and Working\n"; + status += L"?? Package Identity: " + std::wstring(g_isRunningWithIdentity ? L"? Available" : L"? Not Available") + L"\n"; + status += L"?? Sparse Package Support: " + std::wstring(g_isSparsePackageSupported ? L"? Supported" : L"? Not Supported") + L"\n\n"; + + // Test activation + if (g_isRunningWithIdentity) + { + try + { + if (IsShareTargetActivation()) + { + status += L"?? Share Target: ? Currently Activated\n"; + } + else + { + status += L"?? Share Target: ?? Not Currently Activated\n"; + } + } + catch (...) + { + status += L"?? Share Target: ? Error Checking Activation\n"; + } + } + else + { + status += L"?? Share Target: ? Package Identity Required\n"; + } + + status += GetPackageIdentityStatus(); + status += L"\n\n?? WinRT Share Target integration is complete and ready!"; + } + catch (winrt::hresult_error const& ex) + { + status += L"?? WinRT Runtime: ? Error\n"; + status += L"Error: " + std::wstring(ex.message().c_str()) + L"\n"; + status += L"HRESULT: 0x" + std::to_wstring(static_cast(ex.code())) + L"\n"; + } + catch (...) + { + status += L"?? WinRT Runtime: ? Unknown Error\n"; + } + + return status; +} + +void ShareTargetManager::LogShareInfo(const std::wstring& message) +{ + std::wstring logMessage = L"ChatApp: " + message + L"\n"; + OutputDebugStringW(logMessage.c_str()); +} + +void ShareTargetManager::LogShareError(const std::wstring& error) +{ + std::wstring logMessage = L"ChatApp: ShareTarget ERROR - " + error + L"\n"; + OutputDebugStringW(logMessage.c_str()); +} \ No newline at end of file diff --git a/Samples/ChatAppShare/ShareApp/ShareTargetManager.h b/Samples/ChatAppShare/ShareApp/ShareTargetManager.h new file mode 100644 index 0000000..2281410 --- /dev/null +++ b/Samples/ChatAppShare/ShareApp/ShareTargetManager.h @@ -0,0 +1,33 @@ +#pragma once + +#include +#include + +// Simple Share Target Manager with minimal dependencies +class ShareTargetManager +{ +public: + // Initialize the share target manager + static void Initialize(); + + // Check if the current activation is a share target + static bool IsShareTargetActivation(); + + // Check activation arguments and process if it's a share target + static bool ProcessActivationArgs(); + + // Get share target status information + static std::wstring GetShareTargetStatus(); + + // Check if share target is available (requires package identity) + static bool IsShareTargetAvailable(); + +private: + // Logging helpers + static void LogShareInfo(const std::wstring& message); + static void LogShareError(const std::wstring& error); + +private: + static bool s_initialized; + static bool s_shareTargetSupported; +}; \ No newline at end of file diff --git a/Samples/ChatAppShare/ShareApp/UIConstants.h b/Samples/ChatAppShare/ShareApp/UIConstants.h new file mode 100644 index 0000000..ff28af5 --- /dev/null +++ b/Samples/ChatAppShare/ShareApp/UIConstants.h @@ -0,0 +1,23 @@ +#pragma once + +#include + +// Include resource.h for resource constants +#include "resource.h" + +// Modern UI Color Scheme +#define COLOR_PRIMARY RGB(64, 128, 255) // Modern blue +#define COLOR_PRIMARY_DARK RGB(45, 100, 220) // Darker blue for hover +#define COLOR_APP_BACKGROUND RGB(248, 249, 250) // Light gray background +#define COLOR_SURFACE RGB(255, 255, 255) // White surface +#define COLOR_TEXT_PRIMARY RGB(33, 37, 41) // Dark text +#define COLOR_TEXT_SECONDARY RGB(108, 117, 125) // Gray text +#define COLOR_BORDER RGB(222, 226, 230) // Light border +#define COLOR_HOVER RGB(248, 249, 250) // Hover background +#define COLOR_CHAT_BUBBLE_OUT RGB(0, 123, 255) // Outgoing message +#define COLOR_CHAT_BUBBLE_IN RGB(233, 236, 239) // Incoming message + +// UI Constants +#define MAX_LOADSTRING 100 +#define CONTACT_ITEM_HEIGHT 72 +#define AVATAR_SIZE 40 \ No newline at end of file diff --git a/Samples/ChatAppShare/ShareApp/UIManager.cpp b/Samples/ChatAppShare/ShareApp/UIManager.cpp new file mode 100644 index 0000000..bdd4808 --- /dev/null +++ b/Samples/ChatAppShare/ShareApp/UIManager.cpp @@ -0,0 +1,142 @@ +#include "UIManager.h" +#include "UIConstants.h" +#include "ModernUI.h" +#include "ChatModels.h" +#include "FileManager.h" +#include "WindowProcs.h" +#include + +#pragma comment(lib, "comctl32.lib") + +// External declaration - hInst is defined in the main cpp file +extern HINSTANCE hInst; + +// UI Window handles definitions +HWND hContactsList = nullptr; +HWND hChatDisplay = nullptr; +HWND hMessageInput = nullptr; +HWND hSendButton = nullptr; +HWND hContactName = nullptr; +HWND hShareFileButton = nullptr; +HWND hSharedFilesList = nullptr; + +void CreateChatUI(HWND hWnd) +{ + RECT rect; + GetClientRect(hWnd, &rect); + + int width = rect.right - rect.left; + int height = rect.bottom - rect.top; + + // Set window background to modern color + SetClassLongPtr(hWnd, GCLP_HBRBACKGROUND, (LONG_PTR)hBrushBackground); + + // Create contacts list (left panel) with modern styling + hContactsList = CreateWindowEx( + WS_EX_CLIENTEDGE, + L"LISTBOX", NULL, + WS_CHILD | WS_VISIBLE | WS_VSCROLL | LBS_NOTIFY | LBS_OWNERDRAWFIXED, + 20, 20, 280, height - 40, + hWnd, (HMENU)IDC_CONTACTS_LIST, hInst, NULL); + + // Set custom item height for contact list + ::SendMessage(hContactsList, LB_SETITEMHEIGHT, 0, CONTACT_ITEM_HEIGHT); + + // Create contact name label with modern styling + hContactName = CreateWindow(L"STATIC", L"Select a contact to start chatting", + WS_CHILD | WS_VISIBLE | SS_LEFT, + 320, 20, 300, 30, + hWnd, (HMENU)IDC_CONTACT_NAME, hInst, NULL); + + // Create chat display area with modern styling + hChatDisplay = CreateWindowEx( + WS_EX_CLIENTEDGE, + L"EDIT", NULL, + WS_CHILD | WS_VISIBLE | WS_VSCROLL | ES_MULTILINE | ES_READONLY | ES_AUTOVSCROLL, + 320, 60, width - 600, height - 280, + hWnd, (HMENU)IDC_CHAT_DISPLAY, hInst, NULL); + + // Create shared files section header + CreateWindow(L"STATIC", L"Shared Files", + WS_CHILD | WS_VISIBLE | SS_LEFT, + width - 260, 20, 120, 30, + hWnd, NULL, hInst, NULL); + + // Create shared files list with modern styling + hSharedFilesList = CreateWindowEx( + WS_EX_CLIENTEDGE, + L"LISTBOX", NULL, + WS_CHILD | WS_VISIBLE | WS_VSCROLL | LBS_NOTIFY, + width - 260, 60, 240, height - 280, + hWnd, (HMENU)IDC_SHARED_FILES_LIST, hInst, NULL); + + // Create message input with placeholder styling + hMessageInput = CreateWindowEx( + WS_EX_CLIENTEDGE, + L"EDIT", NULL, + WS_CHILD | WS_VISIBLE | ES_MULTILINE | ES_AUTOVSCROLL, + 320, height - 200, width - 600, 60, + hWnd, (HMENU)IDC_MESSAGE_INPUT, hInst, NULL); + + // Create modern styled buttons + hSendButton = CreateWindow(L"BUTTON", L"Send", + WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | BS_OWNERDRAW, + 320, height - 130, 100, 45, + hWnd, (HMENU)IDC_SEND_BUTTON, hInst, NULL); + + hShareFileButton = CreateWindow(L"BUTTON", L"Share File", + WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | BS_OWNERDRAW, + 440, height - 130, 120, 45, + hWnd, (HMENU)IDC_SHARE_FILE_BUTTON, hInst, NULL); + + // Populate contacts list + for (const auto& contact : contacts) { + ::SendMessage(hContactsList, LB_ADDSTRING, 0, (LPARAM)contact.name.c_str()); + } + + // Apply modern fonts to controls + ::SendMessage(hContactName, WM_SETFONT, (WPARAM)hFontTitle, TRUE); + ::SendMessage(hChatDisplay, WM_SETFONT, (WPARAM)hFontRegular, TRUE); + ::SendMessage(hMessageInput, WM_SETFONT, (WPARAM)hFontRegular, TRUE); + ::SendMessage(hSharedFilesList, WM_SETFONT, (WPARAM)hFontRegular, TRUE); + + // Subclass buttons for custom drawing + SetupCustomWindowProcs(); + + // Set modern background colors + SetupWindowColors(); +} + +void ResizeChatUI(HWND hWnd) +{ + RECT rect; + GetClientRect(hWnd, &rect); + int width = rect.right - rect.left; + int height = rect.bottom - rect.top; + + if (hContactsList) { + SetWindowPos(hContactsList, NULL, 20, 20, 280, height - 40, SWP_NOZORDER); + } + if (hChatDisplay) { + SetWindowPos(hChatDisplay, NULL, 320, 60, width - 600, height - 280, SWP_NOZORDER); + } + if (hSharedFilesList) { + SetWindowPos(hSharedFilesList, NULL, width - 260, 60, 240, height - 280, SWP_NOZORDER); + } + if (hMessageInput) { + SetWindowPos(hMessageInput, NULL, 320, height - 200, width - 600, 60, SWP_NOZORDER); + } + if (hSendButton) { + SetWindowPos(hSendButton, NULL, 320, height - 130, 100, 45, SWP_NOZORDER); + } + if (hShareFileButton) { + SetWindowPos(hShareFileButton, NULL, 440, height - 130, 120, 45, SWP_NOZORDER); + } +} + +void SetupWindowColors() +{ + SetClassLongPtr(hChatDisplay, GCLP_HBRBACKGROUND, (LONG_PTR)hBrushSurface); + SetClassLongPtr(hMessageInput, GCLP_HBRBACKGROUND, (LONG_PTR)hBrushSurface); + SetClassLongPtr(hSharedFilesList, GCLP_HBRBACKGROUND, (LONG_PTR)hBrushSurface); +} \ No newline at end of file diff --git a/Samples/ChatAppShare/ShareApp/UIManager.h b/Samples/ChatAppShare/ShareApp/UIManager.h new file mode 100644 index 0000000..74c2a14 --- /dev/null +++ b/Samples/ChatAppShare/ShareApp/UIManager.h @@ -0,0 +1,17 @@ +#pragma once + +#include + +// UI Window handles +extern HWND hContactsList; +extern HWND hChatDisplay; +extern HWND hMessageInput; +extern HWND hSendButton; +extern HWND hContactName; +extern HWND hShareFileButton; +extern HWND hSharedFilesList; + +// UI management functions +void CreateChatUI(HWND hWnd); +void ResizeChatUI(HWND hWnd); +void SetupWindowColors(); \ No newline at end of file diff --git a/Samples/ChatAppShare/ShareApp/WindowProcs.cpp b/Samples/ChatAppShare/ShareApp/WindowProcs.cpp new file mode 100644 index 0000000..ce00862 --- /dev/null +++ b/Samples/ChatAppShare/ShareApp/WindowProcs.cpp @@ -0,0 +1,168 @@ +#include "WindowProcs.h" +#include "UIConstants.h" +#include "ModernUI.h" +#include "ChatModels.h" +#include "UIManager.h" + +// External declarations for UI window handles +extern HWND hSendButton; +extern HWND hShareFileButton; +extern HWND hContactsList; + +// Window procedure storage for subclassing +WNDPROC originalButtonProc = nullptr; +WNDPROC originalListBoxProc = nullptr; + +void SetupCustomWindowProcs() +{ + // Subclass buttons for custom drawing + originalButtonProc = (WNDPROC)SetWindowLongPtr(hSendButton, GWLP_WNDPROC, (LONG_PTR)ModernButtonProc); + SetWindowLongPtr(hShareFileButton, GWLP_WNDPROC, (LONG_PTR)ModernButtonProc); + + // Subclass contact list for custom drawing + originalListBoxProc = (WNDPROC)SetWindowLongPtr(hContactsList, GWLP_WNDPROC, (LONG_PTR)ModernListBoxProc); +} + +// Modern button subclass procedure +LRESULT CALLBACK ModernButtonProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + static bool isHovered = false; + static bool isPressed = false; + + switch (msg) + { + case WM_PAINT: + { + PAINTSTRUCT ps; + HDC hdc = BeginPaint(hWnd, &ps); + + RECT rect; + GetClientRect(hWnd, &rect); + + WCHAR text[256]; + GetWindowText(hWnd, text, 256); + + DrawModernButton(hdc, rect, std::wstring(text), isHovered, isPressed); + + EndPaint(hWnd, &ps); + return 0; + } + + case WM_MOUSEMOVE: + if (!isHovered) + { + isHovered = true; + InvalidateRect(hWnd, NULL, FALSE); + + TRACKMOUSEEVENT tme = {}; + tme.cbSize = sizeof(tme); + tme.dwFlags = TME_LEAVE; + tme.hwndTrack = hWnd; + TrackMouseEvent(&tme); + } + break; + + case WM_MOUSELEAVE: + isHovered = false; + InvalidateRect(hWnd, NULL, FALSE); + break; + + case WM_LBUTTONDOWN: + isPressed = true; + InvalidateRect(hWnd, NULL, FALSE); + SetCapture(hWnd); + break; + + case WM_LBUTTONUP: + if (isPressed) + { + isPressed = false; + InvalidateRect(hWnd, NULL, FALSE); + ReleaseCapture(); + + // Send click notification to parent + HWND hParent = GetParent(hWnd); + int controlId = GetDlgCtrlID(hWnd); + ::SendMessage(hParent, WM_COMMAND, MAKEWPARAM(controlId, BN_CLICKED), (LPARAM)hWnd); + } + break; + } + + return CallWindowProc(originalButtonProc, hWnd, msg, wParam, lParam); +} + +// Modern listbox subclass procedure +LRESULT CALLBACK ModernListBoxProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + switch (msg) + { + case WM_LBUTTONDOWN: + if (hWnd == hContactsList) + { + // Handle mouse click to ensure proper contact selection with scrolling + POINT pt = { LOWORD(lParam), HIWORD(lParam) }; + + // Calculate which contact was clicked based on the scroll position + int topIndex = (int)::SendMessage(hWnd, LB_GETTOPINDEX, 0, 0); + int clickedVisiblePos = pt.y / CONTACT_ITEM_HEIGHT; + int actualContactIndex = topIndex + clickedVisiblePos; + + // Ensure the clicked contact is valid + if (actualContactIndex >= 0 && actualContactIndex < (int)contacts.size()) + { + // Set the correct selection in the listbox + ::SendMessage(hWnd, LB_SETCURSEL, actualContactIndex, 0); + + // Trigger the selection change event to update the UI + HWND hParent = GetParent(hWnd); + int controlId = GetDlgCtrlID(hWnd); + ::SendMessage(hParent, WM_COMMAND, MAKEWPARAM(controlId, LBN_SELCHANGE), (LPARAM)hWnd); + + return 0; // We handled the click + } + } + break; + + case WM_PAINT: + if (hWnd == hContactsList) + { + PAINTSTRUCT ps; + HDC hdc = BeginPaint(hWnd, &ps); + + RECT clientRect; + GetClientRect(hWnd, &clientRect); + + // Fill background + FillRect(hdc, &clientRect, hBrushSurface); + + int itemCount = (int)contacts.size(); + int selectedIndex = (int)::SendMessage(hWnd, LB_GETCURSEL, 0, 0); + + // Get the first visible item index to handle scrolling correctly + int topIndex = (int)::SendMessage(hWnd, LB_GETTOPINDEX, 0, 0); + + // Calculate how many items can be visible + int visibleItemCount = (clientRect.bottom / CONTACT_ITEM_HEIGHT) + 1; + + // Draw only the visible items + for (int visiblePos = 0; visiblePos < visibleItemCount; visiblePos++) + { + int actualIndex = topIndex + visiblePos; + + // Stop if we've reached the end of the contact list + if (actualIndex >= itemCount) break; + + RECT itemRect = {0, visiblePos * CONTACT_ITEM_HEIGHT, clientRect.right, (visiblePos + 1) * CONTACT_ITEM_HEIGHT}; + + // Draw the contact that should be visible at this position + DrawContactItem(hdc, itemRect, contacts[actualIndex], actualIndex == selectedIndex); + } + + EndPaint(hWnd, &ps); + return 0; + } + break; + } + + return CallWindowProc(originalListBoxProc, hWnd, msg, wParam, lParam); +} \ No newline at end of file diff --git a/Samples/ChatAppShare/ShareApp/WindowProcs.h b/Samples/ChatAppShare/ShareApp/WindowProcs.h new file mode 100644 index 0000000..97c8623 --- /dev/null +++ b/Samples/ChatAppShare/ShareApp/WindowProcs.h @@ -0,0 +1,14 @@ +#pragma once + +#include + +// Window procedures +LRESULT CALLBACK ModernButtonProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); +LRESULT CALLBACK ModernListBoxProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); + +// Original window procedures storage +extern WNDPROC originalButtonProc; +extern WNDPROC originalListBoxProc; + +// Setup functions +void SetupCustomWindowProcs(); \ No newline at end of file diff --git a/Samples/ChatAppShare/ShareApp/framework.h b/Samples/ChatAppShare/ShareApp/framework.h new file mode 100644 index 0000000..414c103 --- /dev/null +++ b/Samples/ChatAppShare/ShareApp/framework.h @@ -0,0 +1,29 @@ +// header.h : include file for standard system include files, +// or project specific include files +// + +#pragma once + +#include "targetver.h" +#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers + +// Windows Header Files +#include + +// WinRT Headers for Share Target functionality +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// C RunTime Header Files +#include +#include +#include +#include diff --git a/Samples/ChatAppShare/ShareApp/small.ico b/Samples/ChatAppShare/ShareApp/small.ico new file mode 100644 index 0000000000000000000000000000000000000000..b3ec03bd617f32e58128fa977fd6ac9605124f4b GIT binary patch literal 46227 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 From ade8e1e4f62e731e7825689fb91c767bad2b0b3d Mon Sep 17 00:00:00 2001 From: Ravish Roshan Date: Fri, 21 Nov 2025 05:25:06 +0530 Subject: [PATCH 14/20] Added ShareApp Project --- Samples/ChatAppShare/SampleChatAppWithShare.sln | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/Samples/ChatAppShare/SampleChatAppWithShare.sln b/Samples/ChatAppShare/SampleChatAppWithShare.sln index 8833b0a..d31f7b8 100644 --- a/Samples/ChatAppShare/SampleChatAppWithShare.sln +++ b/Samples/ChatAppShare/SampleChatAppWithShare.sln @@ -1,10 +1,12 @@  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 -VisualStudioVersion = 17.14.36401.2 d17.14 +VisualStudioVersion = 17.14.36401.2 MinimumVisualStudioVersion = 10.0.40219.1 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SampleChatAppWithShare", "SampleChatAppWithShare\SampleChatAppWithShare.vcxproj", "{DD6EC845-790A-4BD4-B638-AF0964704337}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ShareApp", "ShareApp\ShareApp.vcxproj", "{006AFCE4-5393-4696-9319-9FDD3054774E}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|ARM64 = Debug|ARM64 @@ -27,6 +29,18 @@ Global {DD6EC845-790A-4BD4-B638-AF0964704337}.Release|x64.Build.0 = Release|x64 {DD6EC845-790A-4BD4-B638-AF0964704337}.Release|x86.ActiveCfg = Release|Win32 {DD6EC845-790A-4BD4-B638-AF0964704337}.Release|x86.Build.0 = Release|Win32 + {006AFCE4-5393-4696-9319-9FDD3054774E}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {006AFCE4-5393-4696-9319-9FDD3054774E}.Debug|ARM64.Build.0 = Debug|ARM64 + {006AFCE4-5393-4696-9319-9FDD3054774E}.Debug|x64.ActiveCfg = Debug|x64 + {006AFCE4-5393-4696-9319-9FDD3054774E}.Debug|x64.Build.0 = Debug|x64 + {006AFCE4-5393-4696-9319-9FDD3054774E}.Debug|x86.ActiveCfg = Debug|Win32 + {006AFCE4-5393-4696-9319-9FDD3054774E}.Debug|x86.Build.0 = Debug|Win32 + {006AFCE4-5393-4696-9319-9FDD3054774E}.Release|ARM64.ActiveCfg = Release|ARM64 + {006AFCE4-5393-4696-9319-9FDD3054774E}.Release|ARM64.Build.0 = Release|ARM64 + {006AFCE4-5393-4696-9319-9FDD3054774E}.Release|x64.ActiveCfg = Release|x64 + {006AFCE4-5393-4696-9319-9FDD3054774E}.Release|x64.Build.0 = Release|x64 + {006AFCE4-5393-4696-9319-9FDD3054774E}.Release|x86.ActiveCfg = Release|Win32 + {006AFCE4-5393-4696-9319-9FDD3054774E}.Release|x86.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE From 3f6d7f96f5a5525987a1142d39c3e45a118a98db Mon Sep 17 00:00:00 2001 From: Ravish Roshan Date: Fri, 21 Nov 2025 10:51:15 +0530 Subject: [PATCH 15/20] Share app and manifest changes --- Samples/ChatAppShare/AppxManifest.xml | 41 ++ .../SampleChatAppWithShare.cpp | 2 +- .../SampleChatAppWithShare.exe.manifest | 2 +- Samples/ChatAppShare/ShareApp/ShareApp.cpp | 2 +- .../ShareApp/ShareApp.exe.manifest | 2 +- .../ChatAppShare/ShareApp/ShareApp.vcxproj | 2 + .../ShareApp/ShareApp.vcxproj.filters | 2 + .../ShareApp/ShareTargetManager.cpp | 356 +++++++++++++++++- .../ShareApp/ShareTargetManager.h | 15 + .../Weixin_1.0.2.0_x86__v4k3sbdawh17a.msix | Bin 0 -> 6843 bytes 10 files changed, 414 insertions(+), 10 deletions(-) create mode 100644 Samples/ChatAppShare/AppxManifest.xml create mode 100644 Samples/ChatAppShare/Weixin_1.0.2.0_x86__v4k3sbdawh17a.msix diff --git a/Samples/ChatAppShare/AppxManifest.xml b/Samples/ChatAppShare/AppxManifest.xml new file mode 100644 index 0000000..45fd999 --- /dev/null +++ b/Samples/ChatAppShare/AppxManifest.xml @@ -0,0 +1,41 @@ + + + + + Weixin + Weixin + Assets\WeChat150×150minin.png + true + + + + + + + + + + + + + + + + + + + + StorageItems + Bitmap + URI + Text + + + + + + + + + + \ No newline at end of file diff --git a/Samples/ChatAppShare/SampleChatAppWithShare/SampleChatAppWithShare.cpp b/Samples/ChatAppShare/SampleChatAppWithShare/SampleChatAppWithShare.cpp index ff6cdcd..21773b4 100644 --- a/Samples/ChatAppShare/SampleChatAppWithShare/SampleChatAppWithShare.cpp +++ b/Samples/ChatAppShare/SampleChatAppWithShare/SampleChatAppWithShare.cpp @@ -91,7 +91,7 @@ int APIENTRY wWinMain(_In_ HINSTANCE hInstance, if (g_isSparsePackageSupported && !g_isRunningWithIdentity) { std::wstring executableDir = GetExecutableDirectory(); - std::wstring packagePath = executableDir + L"\\Weixin_1.0.1.0_x86__v4k3sbdawh17a.msix"; + std::wstring packagePath = executableDir + L"\\Weixin_1.0.2.0_x86__v4k3sbdawh17a.msix"; // Validate the MSIX package before attempting registration if (ValidateMsixPackage(packagePath)) diff --git a/Samples/ChatAppShare/SampleChatAppWithShare/SampleChatAppWithShare.exe.manifest b/Samples/ChatAppShare/SampleChatAppWithShare/SampleChatAppWithShare.exe.manifest index 92ae86a..7f9aa02 100644 --- a/Samples/ChatAppShare/SampleChatAppWithShare/SampleChatAppWithShare.exe.manifest +++ b/Samples/ChatAppShare/SampleChatAppWithShare/SampleChatAppWithShare.exe.manifest @@ -1,6 +1,6 @@ - + - + + + diff --git a/Samples/ChatAppShare/ShareApp/ShareApp.vcxproj.filters b/Samples/ChatAppShare/ShareApp/ShareApp.vcxproj.filters index 8a47ef7..5c9eb5f 100644 --- a/Samples/ChatAppShare/ShareApp/ShareApp.vcxproj.filters +++ b/Samples/ChatAppShare/ShareApp/ShareApp.vcxproj.filters @@ -114,5 +114,7 @@ + + \ No newline at end of file diff --git a/Samples/ChatAppShare/ShareApp/ShareTargetManager.cpp b/Samples/ChatAppShare/ShareApp/ShareTargetManager.cpp index 82131dc..6477513 100644 --- a/Samples/ChatAppShare/ShareApp/ShareTargetManager.cpp +++ b/Samples/ChatAppShare/ShareApp/ShareTargetManager.cpp @@ -207,16 +207,27 @@ bool ShareTargetManager::ProcessActivationArgs() sharedItems.push_back(L"Unknown content type"); } - // Get the main window handle for dialog parent - HWND hMainWindow = GetActiveWindow(); + // Get the main window handle for dialog parent - try to find or launch SampleChatAppWithShare first + HWND hMainWindow = FindOrLaunchPackageApplication(L"SampleChatAppWithShare.exe"); if (!hMainWindow) { - hMainWindow = GetForegroundWindow(); + LogShareInfo(L"Package application not found/launched, using fallback window detection"); + + // Fallback to original logic + hMainWindow = GetActiveWindow(); + if (!hMainWindow) + { + hMainWindow = GetForegroundWindow(); + } + if (!hMainWindow) + { + // Try to find the main chat application window + hMainWindow = FindWindow(NULL, L"Chat Application"); + } } - if (!hMainWindow) + else { - // Try to find the main chat application window - hMainWindow = FindWindow(NULL, L"Chat Application"); + LogShareInfo(L"Successfully found/launched package application for dialog parenting"); } // Log the number of contacts available for debugging @@ -422,4 +433,337 @@ void ShareTargetManager::LogShareError(const std::wstring& error) { std::wstring logMessage = L"ChatApp: ShareTarget ERROR - " + error + L"\n"; OutputDebugStringW(logMessage.c_str()); +} + +// Helper function to get all processes in the same package using proper Package Manager APIs +std::vector ShareTargetManager::GetPackageProcesses() +{ + std::vector packageProcesses; + + if (!g_isRunningWithIdentity) + { + LogShareError(L"GetPackageProcesses called without package identity"); + return packageProcesses; + } + + try + { + // Get the current package family name first + UINT32 currentPackageFamilyNameLength = 0; + LONG result = GetCurrentPackageFamilyName(¤tPackageFamilyNameLength, nullptr); + + if (result != ERROR_INSUFFICIENT_BUFFER) + { + LogShareError(L"Failed to get current package family name length: " + std::to_wstring(result)); + return packageProcesses; + } + + std::wstring currentPackageFamilyName(currentPackageFamilyNameLength, L'\0'); + result = GetCurrentPackageFamilyName(¤tPackageFamilyNameLength, currentPackageFamilyName.data()); + + if (result != ERROR_SUCCESS) + { + LogShareError(L"Failed to get current package family name: " + std::to_wstring(result)); + return packageProcesses; + } + + // Remove null terminator that GetCurrentPackageFamilyName includes in the length + if (!currentPackageFamilyName.empty() && currentPackageFamilyName.back() == L'\0') + { + currentPackageFamilyName.pop_back(); + } + + LogShareInfo(L"Current package family name: " + currentPackageFamilyName); + + // Enumerate all processes and check their package family names + HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); + if (hSnapshot == INVALID_HANDLE_VALUE) + { + LogShareError(L"Failed to create process snapshot"); + return packageProcesses; + } + + PROCESSENTRY32W processEntry = {}; + processEntry.dwSize = sizeof(PROCESSENTRY32W); + + if (Process32FirstW(hSnapshot, &processEntry)) + { + do + { + if (IsProcessInSamePackage(processEntry.th32ProcessID, currentPackageFamilyName)) + { + packageProcesses.push_back(processEntry.th32ProcessID); + LogShareInfo(L"Found package process: " + std::wstring(processEntry.szExeFile) + + L" (PID: " + std::to_wstring(processEntry.th32ProcessID) + L")"); + } + } while (Process32NextW(hSnapshot, &processEntry)); + } + + CloseHandle(hSnapshot); + } + catch (...) + { + LogShareError(L"Exception in GetPackageProcesses"); + } + + LogShareInfo(L"Found " + std::to_wstring(packageProcesses.size()) + L" processes in the same package"); + return packageProcesses; +} + +// Helper function to check if a process is in the same package using proper Package Manager APIs +bool ShareTargetManager::IsProcessInSamePackage(DWORD processId) +{ + if (!g_isRunningWithIdentity) + return false; + + // Get the current package family name first + UINT32 currentPackageFamilyNameLength = 0; + LONG result = GetCurrentPackageFamilyName(¤tPackageFamilyNameLength, nullptr); + if (result != ERROR_INSUFFICIENT_BUFFER) + return false; + + std::wstring currentPackageFamilyName(currentPackageFamilyNameLength, L'\0'); + result = GetCurrentPackageFamilyName(¤tPackageFamilyNameLength, currentPackageFamilyName.data()); + if (result != ERROR_SUCCESS) + return false; + + // Remove null terminator + if (!currentPackageFamilyName.empty() && currentPackageFamilyName.back() == L'\0') + { + currentPackageFamilyName.pop_back(); + } + + return IsProcessInSamePackage(processId, currentPackageFamilyName); +} + +// Overloaded helper function to check if a process is in the same package +bool ShareTargetManager::IsProcessInSamePackage(DWORD processId, const std::wstring& targetPackageFamilyName) +{ + if (!g_isRunningWithIdentity) + return false; + + try + { + // Open the target process to get its package information + HANDLE hProcess = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, processId); + if (hProcess == NULL) + { + // Process might not be accessible or may have exited + return false; + } + + // Get the package family name for the target process + UINT32 packageFamilyNameLength = 0; + LONG result = GetPackageFamilyName(hProcess, &packageFamilyNameLength, nullptr); + + if (result == APPMODEL_ERROR_NO_PACKAGE) + { + // Process is not packaged + CloseHandle(hProcess); + return false; + } + + if (result != ERROR_INSUFFICIENT_BUFFER) + { + CloseHandle(hProcess); + return false; + } + + std::wstring processPackageFamilyName(packageFamilyNameLength, L'\0'); + result = GetPackageFamilyName(hProcess, &packageFamilyNameLength, processPackageFamilyName.data()); + + CloseHandle(hProcess); + + if (result != ERROR_SUCCESS) + { + return false; + } + + // Remove null terminator + if (!processPackageFamilyName.empty() && processPackageFamilyName.back() == L'\0') + { + processPackageFamilyName.pop_back(); + } + + // Compare package family names + bool isSamePackage = (processPackageFamilyName == targetPackageFamilyName); + + if (isSamePackage) + { + LogShareInfo(L"Process " + std::to_wstring(processId) + L" is in the same package: " + processPackageFamilyName); + } + + return isSamePackage; + } + catch (...) + { + LogShareError(L"Exception in IsProcessInSamePackage for PID: " + std::to_wstring(processId)); + return false; + } +} + +// Helper function to find the main window of the package application +HWND ShareTargetManager::FindPackageApplicationWindow(const std::wstring& windowTitle) +{ + LogShareInfo(L"Searching for window with title: " + windowTitle); + + // First try to find the window by title + HWND hWnd = FindWindowW(NULL, windowTitle.c_str()); + if (hWnd) + { + // Verify it belongs to a process in our package + DWORD processId = 0; + GetWindowThreadProcessId(hWnd, &processId); + + if (IsProcessInSamePackage(processId)) + { + LogShareInfo(L"Found package application window (PID: " + std::to_wstring(processId) + L")"); + return hWnd; + } + else + { + LogShareInfo(L"Found window but not in same package"); + } + } + + // If not found by title, enumerate all windows and check each one + std::vector packageProcesses = GetPackageProcesses(); + + for (DWORD processId : packageProcesses) + { + // Find the main window for this process + struct EnumData + { + DWORD processId; + HWND hWnd; + } enumData = { processId, NULL }; + + EnumWindows([](HWND hWnd, LPARAM lParam) -> BOOL + { + EnumData* pData = reinterpret_cast(lParam); + DWORD windowProcessId = 0; + GetWindowThreadProcessId(hWnd, &windowProcessId); + + if (windowProcessId == pData->processId && IsWindowVisible(hWnd)) + { + // Check if this is a main window (has no parent and is not a tool window) + HWND hParent = GetParent(hWnd); + LONG_PTR exStyle = GetWindowLongPtr(hWnd, GWL_EXSTYLE); + + if (!hParent && !(exStyle & WS_EX_TOOLWINDOW)) + { + pData->hWnd = hWnd; + return FALSE; // Stop enumeration + } + } + return TRUE; // Continue enumeration + }, reinterpret_cast(&enumData)); + + if (enumData.hWnd) + { + LogShareInfo(L"Found main window for package process (PID: " + std::to_wstring(processId) + L")"); + return enumData.hWnd; + } + } + + LogShareInfo(L"No package application window found"); + return NULL; +} + +// Helper function to launch the package application +bool ShareTargetManager::LaunchPackageApplication(const std::wstring& appExecutableName) +{ + LogShareInfo(L"Attempting to launch: " + appExecutableName); + + try + { + SHELLEXECUTEINFOW shellInfo = {}; + shellInfo.cbSize = sizeof(SHELLEXECUTEINFOW); + shellInfo.fMask = SEE_MASK_NOCLOSEPROCESS | SEE_MASK_FLAG_NO_UI; + shellInfo.lpVerb = L"open"; + shellInfo.lpFile = appExecutableName.c_str(); + shellInfo.lpParameters = NULL; + shellInfo.lpDirectory = NULL; + shellInfo.nShow = SW_SHOWNORMAL; + + if (ShellExecuteExW(&shellInfo)) + { + if (shellInfo.hProcess) + { + LogShareInfo(L"Successfully launched " + appExecutableName); + CloseHandle(shellInfo.hProcess); + return true; + } + } + + DWORD error = GetLastError(); + LogShareError(L"Failed to launch " + appExecutableName + L" (Error: " + std::to_wstring(error) + L")"); + return false; + } + catch (...) + { + LogShareError(L"Exception in LaunchPackageApplication"); + return false; + } +} + +// Helper function to wait for the application window to appear +HWND ShareTargetManager::WaitForApplicationWindow(const std::wstring& windowTitle, DWORD timeoutMs) +{ + LogShareInfo(L"Waiting for application window: " + windowTitle + L" (timeout: " + std::to_wstring(timeoutMs) + L"ms)"); + + DWORD startTime = GetTickCount(); + HWND hWnd = NULL; + + while ((GetTickCount() - startTime) < timeoutMs) + { + hWnd = FindPackageApplicationWindow(windowTitle); + if (hWnd) + { + LogShareInfo(L"Application window found after " + std::to_wstring(GetTickCount() - startTime) + L"ms"); + return hWnd; + } + + Sleep(250); // Check every 250ms + } + + LogShareError(L"Timeout waiting for application window"); + return NULL; +} + +// Main function to find or launch the package application +HWND ShareTargetManager::FindOrLaunchPackageApplication(const std::wstring& appExecutableName) +{ + LogShareInfo(L"FindOrLaunchPackageApplication called for: " + appExecutableName); + + // First try to find an existing window + HWND hWnd = FindPackageApplicationWindow(L"Chat Application"); + if (hWnd) + { + LogShareInfo(L"Found existing Chat Application window"); + return hWnd; + } + + // If not found, try to launch the application + LogShareInfo(L"Chat Application not found, attempting to launch"); + if (LaunchPackageApplication(appExecutableName)) + { + // Wait for the application window to appear + hWnd = WaitForApplicationWindow(L"Chat Application", 10000); + if (hWnd) + { + LogShareInfo(L"Successfully launched and found Chat Application window"); + return hWnd; + } + else + { + LogShareError(L"Application launched but window not found"); + } + } + else + { + LogShareError(L"Failed to launch application"); + } + + return NULL; } \ No newline at end of file diff --git a/Samples/ChatAppShare/ShareApp/ShareTargetManager.h b/Samples/ChatAppShare/ShareApp/ShareTargetManager.h index 2281410..bd6c2df 100644 --- a/Samples/ChatAppShare/ShareApp/ShareTargetManager.h +++ b/Samples/ChatAppShare/ShareApp/ShareTargetManager.h @@ -2,6 +2,12 @@ #include #include +#include +#include +#include +#include + +#pragma comment(lib, "shell32.lib") // Simple Share Target Manager with minimal dependencies class ShareTargetManager @@ -27,6 +33,15 @@ class ShareTargetManager static void LogShareInfo(const std::wstring& message); static void LogShareError(const std::wstring& error); + // Package application discovery and launch helpers using proper Package Manager APIs + static std::vector GetPackageProcesses(); + static bool IsProcessInSamePackage(DWORD processId); + static bool IsProcessInSamePackage(DWORD processId, const std::wstring& targetPackageFamilyName); + static HWND FindPackageApplicationWindow(const std::wstring& windowTitle); + static bool LaunchPackageApplication(const std::wstring& appExecutableName); + static HWND WaitForApplicationWindow(const std::wstring& windowTitle, DWORD timeoutMs = 10000); + static HWND FindOrLaunchPackageApplication(const std::wstring& appExecutableName); + private: static bool s_initialized; static bool s_shareTargetSupported; diff --git a/Samples/ChatAppShare/Weixin_1.0.2.0_x86__v4k3sbdawh17a.msix b/Samples/ChatAppShare/Weixin_1.0.2.0_x86__v4k3sbdawh17a.msix new file mode 100644 index 0000000000000000000000000000000000000000..b5c2681a5b4b8eee93a26190af919395811cfc60 GIT binary patch literal 6843 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

oa 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 h1dbYTp?fT0^}Yq4Oqjlxan(8+gx|zZej@ Date: Fri, 21 Nov 2025 12:05:40 +0530 Subject: [PATCH 16/20] Enhance README with ShareApp and Packaging details Added sections for ShareApp and Packaging in README. --- Samples/ChatAppShare/README.md | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/Samples/ChatAppShare/README.md b/Samples/ChatAppShare/README.md index c378030..4d1a81e 100644 --- a/Samples/ChatAppShare/README.md +++ b/Samples/ChatAppShare/README.md @@ -13,14 +13,17 @@ You can learn more about Signed Sparse Packages and Identity, Registration & Act ### SampleChatAppWithShare A non-package native Windows desktop Win32 GUI application written in C++. It installs a Signed Sparse Package and uses it to act as a Share Target. - * Registration of a Signed Sparse Package happens in PackageIdentity.cpp. +### ShareApp * Handling of Shared Data happens in ShareTargetManager.cpp. -* Files to package and sign to create a Sparse Package for use with the app are located in the PackageWithExternalLocationCppSample directory inside PackageWithExternalLocation sample. - +* Registration of a Signed Sparse Package happens in PackageIdentity.cpp. +### Packaging +* Manifest file to package both apps as external location is present in AppxManifest.xml at the root of this sample folder. For reference we have an MSIX as well. + + ### Building and running the sample 1. Make sure your machine has Developer Mode turned on. @@ -38,4 +41,4 @@ A non-package native Windows desktop Win32 GUI application written in C++. It in ### Removing the package If you need to remove the package from package manager you can run the following command in an admin command prompt: -powershell -c “get-appxpackage -name \*PackageWithExternalLocationCppSample\* | remove-appxpackage" \ No newline at end of file +powershell -c “get-appxpackage -name \*PackageWithExternalLocationCppSample\* | remove-appxpackage" From ceba1600ec2b38c28502c713976117ee82857e12 Mon Sep 17 00:00:00 2001 From: Ravish Roshan Date: Sun, 23 Nov 2025 15:39:26 +0530 Subject: [PATCH 17/20] Launch main app if not running already --- Samples/ChatAppShare/ShareApp/ShareTargetManager.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Samples/ChatAppShare/ShareApp/ShareTargetManager.cpp b/Samples/ChatAppShare/ShareApp/ShareTargetManager.cpp index 6477513..e18b183 100644 --- a/Samples/ChatAppShare/ShareApp/ShareTargetManager.cpp +++ b/Samples/ChatAppShare/ShareApp/ShareTargetManager.cpp @@ -677,15 +677,20 @@ bool ShareTargetManager::LaunchPackageApplication(const std::wstring& appExecuta try { + // Get the current executable directory to use as working directory + std::wstring executableDir = GetExecutableDirectory(); + SHELLEXECUTEINFOW shellInfo = {}; shellInfo.cbSize = sizeof(SHELLEXECUTEINFOW); shellInfo.fMask = SEE_MASK_NOCLOSEPROCESS | SEE_MASK_FLAG_NO_UI; shellInfo.lpVerb = L"open"; shellInfo.lpFile = appExecutableName.c_str(); shellInfo.lpParameters = NULL; - shellInfo.lpDirectory = NULL; + shellInfo.lpDirectory = executableDir.empty() ? NULL : executableDir.c_str(); shellInfo.nShow = SW_SHOWNORMAL; + LogShareInfo(L"Launch directory: " + (executableDir.empty() ? L"[NULL - using current directory]" : executableDir)); + if (ShellExecuteExW(&shellInfo)) { if (shellInfo.hProcess) From 1ebc860c04b53ffa4522731d97e99f25fd22017f Mon Sep 17 00:00:00 2001 From: Ravish Roshan Date: Sun, 23 Nov 2025 22:32:14 +0530 Subject: [PATCH 18/20] Fetch contact only for share flow. --- .../ShareApp/ShareTargetManager.cpp | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/Samples/ChatAppShare/ShareApp/ShareTargetManager.cpp b/Samples/ChatAppShare/ShareApp/ShareTargetManager.cpp index e18b183..07d85ea 100644 --- a/Samples/ChatAppShare/ShareApp/ShareTargetManager.cpp +++ b/Samples/ChatAppShare/ShareApp/ShareTargetManager.cpp @@ -93,13 +93,6 @@ bool ShareTargetManager::ProcessActivationArgs() LogShareInfo(L"? Share Target activation detected!"); s_alreadyProcessed = true; // Mark as processed - // Ensure contacts are initialized (critical for share target scenarios) - if (contacts.empty()) - { - LogShareInfo(L"Initializing contacts for share target scenario..."); - InitializeContacts(); - } - // Process the share target directly here auto shareArgs = activationArgs.as(); auto shareOperation = shareArgs.ShareOperation(); @@ -230,6 +223,15 @@ bool ShareTargetManager::ProcessActivationArgs() LogShareInfo(L"Successfully found/launched package application for dialog parenting"); } + // Ensure contacts are initialized (critical for share target scenarios) + if (contacts.empty()) + { + LogShareInfo(L"Initializing contacts for share target scenario..."); + + // [TODO] Get contacts from the main app. + InitializeContacts(); + } + // Log the number of contacts available for debugging LogShareInfo(L"Available contacts: " + std::to_wstring(contacts.size())); @@ -275,6 +277,7 @@ bool ShareTargetManager::ProcessActivationArgs() selectedContact.lastMessage = lastMsg.length() > 50 ? lastMsg.substr(0, 47) + L"..." : lastMsg; } + // [TODO] Store path somewhere for main application to pick. // If files were shared, add them to the contact's shared files list if (hasFiles && data.Contains(winrt::Windows::ApplicationModel::DataTransfer::StandardDataFormats::StorageItems())) { From 54bdca587abf93ab83f9c744c05fab287e19559b Mon Sep 17 00:00:00 2001 From: Ravish Roshan Date: Tue, 25 Nov 2025 13:51:02 +0530 Subject: [PATCH 19/20] Added background service --- .../BackgroundService.cpp | 100 ++++++++++++++++++ .../BackgroundService.h | 27 +++++ .../SampleChatAppWithShare.cpp | 12 ++- .../SampleChatAppWithShare.vcxproj | 3 + .../SampleChatAppWithShare.vcxproj.filters | 28 +++++ .../ShareApp/HelloWorldClient.cpp | 67 ++++++++++++ .../ChatAppShare/ShareApp/HelloWorldClient.h | 25 +++++ Samples/ChatAppShare/ShareApp/ShareApp.cpp | 13 +++ .../ChatAppShare/ShareApp/ShareApp.vcxproj | 2 + .../ShareApp/ShareApp.vcxproj.filters | 6 ++ 10 files changed, 282 insertions(+), 1 deletion(-) create mode 100644 Samples/ChatAppShare/SampleChatAppWithShare/BackgroundService.cpp create mode 100644 Samples/ChatAppShare/SampleChatAppWithShare/BackgroundService.h create mode 100644 Samples/ChatAppShare/ShareApp/HelloWorldClient.cpp create mode 100644 Samples/ChatAppShare/ShareApp/HelloWorldClient.h diff --git a/Samples/ChatAppShare/SampleChatAppWithShare/BackgroundService.cpp b/Samples/ChatAppShare/SampleChatAppWithShare/BackgroundService.cpp new file mode 100644 index 0000000..4b01cee --- /dev/null +++ b/Samples/ChatAppShare/SampleChatAppWithShare/BackgroundService.cpp @@ -0,0 +1,100 @@ +#include "BackgroundService.h" +#include + +// Global constants definition +const std::string PIPE_NAME = "\\\\.\\pipe\\HelloWorldService"; +const std::string HELLO_WORLD_RESPONSE = "Hello world"; + +HelloWorldService::HelloWorldService() : m_running(false) {} + +HelloWorldService::~HelloWorldService() { + Stop(); +} + +bool HelloWorldService::Start() { + if (m_running) { + return false; + } + + m_running = true; + m_serviceThread = std::thread(&HelloWorldService::ServiceLoop, this); + + std::cout << "Hello World Service started. Listening on pipe: " << PIPE_NAME << std::endl; + return true; +} + +void HelloWorldService::Stop() { + if (m_running) { + m_running = false; + if (m_serviceThread.joinable()) { + m_serviceThread.join(); + } + std::cout << "Hello World Service stopped." << std::endl; + } +} + +bool HelloWorldService::IsRunning() const { + return m_running; +} + +void HelloWorldService::ServiceLoop() { + while (m_running) { + // Create named pipe + HANDLE hPipe = CreateNamedPipeA( + PIPE_NAME.c_str(), + PIPE_ACCESS_DUPLEX, + PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT, + 1, // Max instances + 1024, // Output buffer size + 1024, // Input buffer size + 0, // Default timeout + nullptr // Security attributes + ); + + if (hPipe == INVALID_HANDLE_VALUE) { + std::cerr << "Failed to create named pipe. Error: " << GetLastError() << std::endl; + continue; + } + + // Wait for client connection + std::cout << "Waiting for client connection..." << std::endl; + + if (ConnectNamedPipe(hPipe, nullptr) || GetLastError() == ERROR_PIPE_CONNECTED) { + std::cout << "Client connected!" << std::endl; + HandleClient(hPipe); + } + else { + std::cerr << "Failed to connect to client. Error: " << GetLastError() << std::endl; + } + + CloseHandle(hPipe); + } +} + +void HelloWorldService::HandleClient(HANDLE hPipe) { + char buffer[1024]; + DWORD bytesRead; + DWORD bytesWritten; + + // Read request from client + if (ReadFile(hPipe, buffer, sizeof(buffer) - 1, &bytesRead, nullptr)) { + buffer[bytesRead] = '\0'; + std::cout << "Received request: " << buffer << std::endl; + + // Send "Hello world" response + const char* response = HELLO_WORLD_RESPONSE.c_str(); + if (WriteFile(hPipe, response, static_cast(HELLO_WORLD_RESPONSE.length()), &bytesWritten, nullptr)) { + std::cout << "Sent response: " << response << std::endl; + } + else { + std::cerr << "Failed to send response. Error: " << GetLastError() << std::endl; + } + } + else { + std::cerr << "Failed to read from client. Error: " << GetLastError() << std::endl; + } + + // Flush the pipe to allow the client to read the pipe's contents before disconnecting + FlushFileBuffers(hPipe); + DisconnectNamedPipe(hPipe); +} \ No newline at end of file diff --git a/Samples/ChatAppShare/SampleChatAppWithShare/BackgroundService.h b/Samples/ChatAppShare/SampleChatAppWithShare/BackgroundService.h new file mode 100644 index 0000000..a7f81a2 --- /dev/null +++ b/Samples/ChatAppShare/SampleChatAppWithShare/BackgroundService.h @@ -0,0 +1,27 @@ +#pragma once + +#include +#include +#include + +// Forward declarations and constants +extern const std::string PIPE_NAME; +extern const std::string HELLO_WORLD_RESPONSE; + +class HelloWorldService { +private: + bool m_running; + std::thread m_serviceThread; + +public: + HelloWorldService(); + ~HelloWorldService(); + + bool Start(); + void Stop(); + bool IsRunning() const; + +private: + void ServiceLoop(); + void HandleClient(HANDLE hPipe); +}; diff --git a/Samples/ChatAppShare/SampleChatAppWithShare/SampleChatAppWithShare.cpp b/Samples/ChatAppShare/SampleChatAppWithShare/SampleChatAppWithShare.cpp index 21773b4..b6a14e0 100644 --- a/Samples/ChatAppShare/SampleChatAppWithShare/SampleChatAppWithShare.cpp +++ b/Samples/ChatAppShare/SampleChatAppWithShare/SampleChatAppWithShare.cpp @@ -11,6 +11,7 @@ #include "UIManager.h" #include "PackageIdentity.h" #include "ShareTargetManager.h" +#include "BackgroundService.h" #include "WindowProcs.h" #include #include @@ -57,6 +58,7 @@ int APIENTRY wWinMain(_In_ HINSTANCE hInstance, // Init package identity checks InitializePackageIdentity(); + HelloWorldService service; // Initialize WinRT with proper error handling try @@ -118,6 +120,11 @@ int APIENTRY wWinMain(_In_ HINSTANCE hInstance, } else if (g_isRunningWithIdentity) { + HelloWorldService service; + + if (!service.Start()) { + return 1; + } // Process share target activation using the ShareTargetManager ShareTargetManager::ProcessActivationArgs(); } @@ -190,7 +197,10 @@ int APIENTRY wWinMain(_In_ HINSTANCE hInstance, DispatchMessage(&msg); } } - + if (service.IsRunning()) + { + service.Stop(); + } CoUninitialize(); return (int) msg.wParam; } diff --git a/Samples/ChatAppShare/SampleChatAppWithShare/SampleChatAppWithShare.vcxproj b/Samples/ChatAppShare/SampleChatAppWithShare/SampleChatAppWithShare.vcxproj index 131e855..3adf298 100644 --- a/Samples/ChatAppShare/SampleChatAppWithShare/SampleChatAppWithShare.vcxproj +++ b/Samples/ChatAppShare/SampleChatAppWithShare/SampleChatAppWithShare.vcxproj @@ -182,6 +182,7 @@ + @@ -198,6 +199,7 @@ + @@ -220,6 +222,7 @@ + diff --git a/Samples/ChatAppShare/SampleChatAppWithShare/SampleChatAppWithShare.vcxproj.filters b/Samples/ChatAppShare/SampleChatAppWithShare/SampleChatAppWithShare.vcxproj.filters index 1bc4c91..781bdd3 100644 --- a/Samples/ChatAppShare/SampleChatAppWithShare/SampleChatAppWithShare.vcxproj.filters +++ b/Samples/ChatAppShare/SampleChatAppWithShare/SampleChatAppWithShare.vcxproj.filters @@ -51,6 +51,15 @@ Header Files + + Header Files + + + Header Files + + + Header Files + @@ -77,6 +86,15 @@ Source Files + + Source Files + + + Source Files + + + Source Files + @@ -94,4 +112,14 @@ + + + + + + + + + + \ No newline at end of file diff --git a/Samples/ChatAppShare/ShareApp/HelloWorldClient.cpp b/Samples/ChatAppShare/ShareApp/HelloWorldClient.cpp new file mode 100644 index 0000000..6fa189d --- /dev/null +++ b/Samples/ChatAppShare/ShareApp/HelloWorldClient.cpp @@ -0,0 +1,67 @@ +#include "HelloWorldClient.h" +#include +#include + +const std::string HelloWorldClient::PIPE_NAME = "\\\\.\\pipe\\HelloWorldService"; + +std::string HelloWorldClient::GetHelloWorld() { + HANDLE hPipe; + char buffer[1024]; + DWORD bytesRead, bytesWritten; + + // Try to open the named pipe + while (true) { + hPipe = CreateFileA( + PIPE_NAME.c_str(), + GENERIC_READ | GENERIC_WRITE, + 0, + nullptr, + OPEN_EXISTING, + 0, + nullptr + ); + + if (hPipe != INVALID_HANDLE_VALUE) { + break; + } + + if (GetLastError() != ERROR_PIPE_BUSY) { + std::cerr << "Could not open pipe. Error: " << GetLastError() << std::endl; + return ""; + } + + // All pipe instances are busy, wait a bit + if (!WaitNamedPipeA(PIPE_NAME.c_str(), TIMEOUT_MS)) { + std::cerr << "Could not open pipe: timeout after " << TIMEOUT_MS << "ms." << std::endl; + return ""; + } + } + + // Set pipe to message-read mode + DWORD dwMode = PIPE_READMODE_MESSAGE; + if (!SetNamedPipeHandleState(hPipe, &dwMode, nullptr, nullptr)) { + std::cerr << "SetNamedPipeHandleState failed. Error: " << GetLastError() << std::endl; + CloseHandle(hPipe); + return ""; + } + + // Send a request message to the pipe server + const char* message = "GET_HELLO_WORLD"; + if (!WriteFile(hPipe, message, static_cast(strlen(message)), &bytesWritten, nullptr)) { + std::cerr << "WriteFile to pipe failed. Error: " << GetLastError() << std::endl; + CloseHandle(hPipe); + return ""; + } + + // Read the response from the server + if (ReadFile(hPipe, buffer, sizeof(buffer) - 1, &bytesRead, nullptr)) { + buffer[bytesRead] = '\0'; + CloseHandle(hPipe); + return std::string(buffer); + } + else { + std::cerr << "ReadFile from pipe failed. Error: " << GetLastError() << std::endl; + CloseHandle(hPipe); + return ""; + } +} \ No newline at end of file diff --git a/Samples/ChatAppShare/ShareApp/HelloWorldClient.h b/Samples/ChatAppShare/ShareApp/HelloWorldClient.h new file mode 100644 index 0000000..900eb72 --- /dev/null +++ b/Samples/ChatAppShare/ShareApp/HelloWorldClient.h @@ -0,0 +1,25 @@ +#pragma once +#include +#include + +/** + * HelloWorldClient - A simple client class to communicate with the HelloWorld background service + * + * Usage: + * std::string response = HelloWorldClient::GetHelloWorld(); + * if (!response.empty()) { + * // Use the response + * } + */ +class HelloWorldClient { +public: + /** + * Connects to the HelloWorld background service and requests "Hello world" + * @return The response from the service, or empty string if failed + */ + static std::string GetHelloWorld(); + +private: + static const std::string PIPE_NAME; + static const int TIMEOUT_MS = 20000; // 20 seconds +}; \ No newline at end of file diff --git a/Samples/ChatAppShare/ShareApp/ShareApp.cpp b/Samples/ChatAppShare/ShareApp/ShareApp.cpp index 50f9d82..65cceb9 100644 --- a/Samples/ChatAppShare/ShareApp/ShareApp.cpp +++ b/Samples/ChatAppShare/ShareApp/ShareApp.cpp @@ -11,6 +11,7 @@ #include "UIManager.h" #include "PackageIdentity.h" #include "ShareTargetManager.h" +#include "HelloWorldClient.h" #include "WindowProcs.h" #include #include @@ -77,6 +78,8 @@ int APIENTRY wWinMain(_In_ HINSTANCE hInstance, // Initialize Share Target Manager ShareTargetManager::Initialize(); + // Add 3 mins sleep to allow time for service to start when debugging + Sleep(180000); // Note: Don't process share target activation here - UI isn't created yet // This will be handled after UI creation in WndProc WM_CREATE @@ -118,6 +121,16 @@ int APIENTRY wWinMain(_In_ HINSTANCE hInstance, } else if (g_isRunningWithIdentity) { + std::string response = HelloWorldClient::GetHelloWorld(); + + if (!response.empty()) { + OutputDebugStringW(L"Response from service: "); + } + else { + OutputDebugStringW(L"Failed to get response from service."); + OutputDebugStringW(L"Make sure the HelloWorld service is running."); + } + // Process share target activation using the ShareTargetManager ShareTargetManager::ProcessActivationArgs(); } diff --git a/Samples/ChatAppShare/ShareApp/ShareApp.vcxproj b/Samples/ChatAppShare/ShareApp/ShareApp.vcxproj index 985a3c5..b230869 100644 --- a/Samples/ChatAppShare/ShareApp/ShareApp.vcxproj +++ b/Samples/ChatAppShare/ShareApp/ShareApp.vcxproj @@ -187,6 +187,7 @@ + @@ -202,6 +203,7 @@ + diff --git a/Samples/ChatAppShare/ShareApp/ShareApp.vcxproj.filters b/Samples/ChatAppShare/ShareApp/ShareApp.vcxproj.filters index 5c9eb5f..a44014c 100644 --- a/Samples/ChatAppShare/ShareApp/ShareApp.vcxproj.filters +++ b/Samples/ChatAppShare/ShareApp/ShareApp.vcxproj.filters @@ -57,6 +57,9 @@ Header Files + + Header Files + @@ -89,6 +92,9 @@ Source Files + + Source Files + From b9ca5cff89d3dda06b6e72f66227c438dc548380 Mon Sep 17 00:00:00 2001 From: Ravish Roshan Date: Tue, 25 Nov 2025 13:52:23 +0530 Subject: [PATCH 20/20] Removed sleep which was added for debugging --- Samples/ChatAppShare/ShareApp/ShareApp.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/Samples/ChatAppShare/ShareApp/ShareApp.cpp b/Samples/ChatAppShare/ShareApp/ShareApp.cpp index 65cceb9..d0ecc24 100644 --- a/Samples/ChatAppShare/ShareApp/ShareApp.cpp +++ b/Samples/ChatAppShare/ShareApp/ShareApp.cpp @@ -79,7 +79,6 @@ int APIENTRY wWinMain(_In_ HINSTANCE hInstance, // Initialize Share Target Manager ShareTargetManager::Initialize(); // Add 3 mins sleep to allow time for service to start when debugging - Sleep(180000); // Note: Don't process share target activation here - UI isn't created yet // This will be handled after UI creation in WndProc WM_CREATE