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/README.md b/Samples/ChatAppShare/README.md new file mode 100644 index 0000000..4d1a81e --- /dev/null +++ b/Samples/ChatAppShare/README.md @@ -0,0 +1,44 @@ +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. + +### ShareApp +* Handling of Shared Data happens in ShareTargetManager.cpp. + +* 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. +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" diff --git a/Samples/ChatAppShare/SampleChatAppWithShare.sln b/Samples/ChatAppShare/SampleChatAppWithShare.sln new file mode 100644 index 0000000..d31f7b8 --- /dev/null +++ b/Samples/ChatAppShare/SampleChatAppWithShare.sln @@ -0,0 +1,51 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +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 + 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 + {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 + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {6709D164-3EDF-4B1A-894A-2C561B2062CC} + EndGlobalSection +EndGlobal 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/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..b6a14e0 --- /dev/null +++ b/Samples/ChatAppShare/SampleChatAppWithShare/SampleChatAppWithShare.cpp @@ -0,0 +1,467 @@ +// 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 "BackgroundService.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(); + HelloWorldService service; + + // 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.2.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) + { + HelloWorldService service; + + if (!service.Start()) { + return 1; + } + // 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); + } + } + if (service.IsRunning()) + { + service.Stop(); + } + 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..7f9aa02 --- /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 0000000..b3ec03b Binary files /dev/null and b/Samples/ChatAppShare/SampleChatAppWithShare/SampleChatAppWithShare.ico differ diff --git a/Samples/ChatAppShare/SampleChatAppWithShare/SampleChatAppWithShare.rc b/Samples/ChatAppShare/SampleChatAppWithShare/SampleChatAppWithShare.rc new file mode 100644 index 0000000..966faca Binary files /dev/null and b/Samples/ChatAppShare/SampleChatAppWithShare/SampleChatAppWithShare.rc differ diff --git a/Samples/ChatAppShare/SampleChatAppWithShare/SampleChatAppWithShare.vcxproj b/Samples/ChatAppShare/SampleChatAppWithShare/SampleChatAppWithShare.vcxproj new file mode 100644 index 0000000..3adf298 --- /dev/null +++ b/Samples/ChatAppShare/SampleChatAppWithShare/SampleChatAppWithShare.vcxproj @@ -0,0 +1,237 @@ + + + + + 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..781bdd3 --- /dev/null +++ b/Samples/ChatAppShare/SampleChatAppWithShare/SampleChatAppWithShare.vcxproj.filters @@ -0,0 +1,125 @@ + + + + + {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 + + + Header Files + + + + + Source 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/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 0000000..b3ec03b Binary files /dev/null and b/Samples/ChatAppShare/SampleChatAppWithShare/small.ico differ diff --git a/Samples/ChatAppShare/SampleChatAppWithShare/targetver.h b/Samples/ChatAppShare/SampleChatAppWithShare/targetver.h new file mode 100644 index 0000000..bf75e08 --- /dev/null +++ b/Samples/ChatAppShare/SampleChatAppWithShare/targetver.h @@ -0,0 +1,6 @@ +#pragma once + +// // Including SDKDDKVer.h defines the highest available Windows platform. +// If you wish to build your application for a previous Windows platform, include WinSDKVer.h and +// set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h. +#include 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/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/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..d0ecc24 --- /dev/null +++ b/Samples/ChatAppShare/ShareApp/ShareApp.cpp @@ -0,0 +1,469 @@ +// 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 "HelloWorldClient.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(); + // Add 3 mins sleep to allow time for service to start when debugging + + // 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.2.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) + { + 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(); + } + 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..0247996 --- /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 0000000..b3ec03b Binary files /dev/null and b/Samples/ChatAppShare/ShareApp/ShareApp.ico differ diff --git a/Samples/ChatAppShare/ShareApp/ShareApp.rc b/Samples/ChatAppShare/ShareApp/ShareApp.rc new file mode 100644 index 0000000..83cf040 Binary files /dev/null and b/Samples/ChatAppShare/ShareApp/ShareApp.rc differ diff --git a/Samples/ChatAppShare/ShareApp/ShareApp.vcxproj b/Samples/ChatAppShare/ShareApp/ShareApp.vcxproj new file mode 100644 index 0000000..b230869 --- /dev/null +++ b/Samples/ChatAppShare/ShareApp/ShareApp.vcxproj @@ -0,0 +1,238 @@ + + + + + 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..a44014c --- /dev/null +++ b/Samples/ChatAppShare/ShareApp/ShareApp.vcxproj.filters @@ -0,0 +1,126 @@ + + + + + {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 + + + Header Files + + + + + Source 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..07d85ea --- /dev/null +++ b/Samples/ChatAppShare/ShareApp/ShareTargetManager.cpp @@ -0,0 +1,777 @@ +#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 + + // 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 - try to find or launch SampleChatAppWithShare first + HWND hMainWindow = FindOrLaunchPackageApplication(L"SampleChatAppWithShare.exe"); + if (!hMainWindow) + { + 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"); + } + } + else + { + 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())); + + // 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; + } + + // [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())) + { + 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()); +} + +// 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 + { + // 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 = 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) + { + 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 new file mode 100644 index 0000000..bd6c2df --- /dev/null +++ b/Samples/ChatAppShare/ShareApp/ShareTargetManager.h @@ -0,0 +1,48 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +#pragma comment(lib, "shell32.lib") + +// 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); + + // 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; +}; \ 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 0000000..b3ec03b Binary files /dev/null and b/Samples/ChatAppShare/ShareApp/small.ico differ diff --git a/Samples/ChatAppShare/ShareApp/targetver.h b/Samples/ChatAppShare/ShareApp/targetver.h new file mode 100644 index 0000000..bf75e08 --- /dev/null +++ b/Samples/ChatAppShare/ShareApp/targetver.h @@ -0,0 +1,6 @@ +#pragma once + +// // Including SDKDDKVer.h defines the highest available Windows platform. +// If you wish to build your application for a previous Windows platform, include WinSDKVer.h and +// set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h. +#include 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 0000000..b5c2681 Binary files /dev/null and b/Samples/ChatAppShare/Weixin_1.0.2.0_x86__v4k3sbdawh17a.msix differ 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 0000000..d78fac5 Binary files /dev/null and b/Samples/PackageWithExternalLocation/cppwinrt/PackageWithExternalLocationCppSample/resources.pri differ 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 diff --git a/Samples/SampleChatAppWithShare/README.md b/Samples/SampleChatAppWithShare/README.md new file mode 100644 index 0000000..c378030 --- /dev/null +++ b/Samples/SampleChatAppWithShare/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/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/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/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 new file mode 100644 index 0000000..31ace5f --- /dev/null +++ b/Samples/SampleChatAppWithShare/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/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/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/Resource.h b/Samples/SampleChatAppWithShare/SampleChatAppWithShare/Resource.h new file mode 100644 index 0000000..e41d7d3 --- /dev/null +++ b/Samples/SampleChatAppWithShare/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/SampleChatAppWithShare/SampleChatAppWithShare/SampleChatAppWithShare.cpp b/Samples/SampleChatAppWithShare/SampleChatAppWithShare/SampleChatAppWithShare.cpp new file mode 100644 index 0000000..ff6cdcd --- /dev/null +++ b/Samples/SampleChatAppWithShare/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/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.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 0000000..b3ec03b Binary files /dev/null and b/Samples/SampleChatAppWithShare/SampleChatAppWithShare/SampleChatAppWithShare.ico differ diff --git a/Samples/SampleChatAppWithShare/SampleChatAppWithShare/SampleChatAppWithShare.rc b/Samples/SampleChatAppWithShare/SampleChatAppWithShare/SampleChatAppWithShare.rc new file mode 100644 index 0000000..966faca Binary files /dev/null and b/Samples/SampleChatAppWithShare/SampleChatAppWithShare/SampleChatAppWithShare.rc differ diff --git a/Samples/SampleChatAppWithShare/SampleChatAppWithShare/SampleChatAppWithShare.vcxproj b/Samples/SampleChatAppWithShare/SampleChatAppWithShare/SampleChatAppWithShare.vcxproj new file mode 100644 index 0000000..131e855 --- /dev/null +++ b/Samples/SampleChatAppWithShare/SampleChatAppWithShare/SampleChatAppWithShare.vcxproj @@ -0,0 +1,234 @@ + + + + + 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/SampleChatAppWithShare/SampleChatAppWithShare/SampleChatAppWithShare.vcxproj.filters b/Samples/SampleChatAppWithShare/SampleChatAppWithShare/SampleChatAppWithShare.vcxproj.filters new file mode 100644 index 0000000..1bc4c91 --- /dev/null +++ b/Samples/SampleChatAppWithShare/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/SampleChatAppWithShare/SampleChatAppWithShare/ShareTargetManager.cpp b/Samples/SampleChatAppWithShare/SampleChatAppWithShare/ShareTargetManager.cpp new file mode 100644 index 0000000..82131dc --- /dev/null +++ b/Samples/SampleChatAppWithShare/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/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/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..ce00862 --- /dev/null +++ b/Samples/SampleChatAppWithShare/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/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 diff --git a/Samples/SampleChatAppWithShare/SampleChatAppWithShare/framework.h b/Samples/SampleChatAppWithShare/SampleChatAppWithShare/framework.h new file mode 100644 index 0000000..414c103 --- /dev/null +++ b/Samples/SampleChatAppWithShare/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/SampleChatAppWithShare/SampleChatAppWithShare/small.ico b/Samples/SampleChatAppWithShare/SampleChatAppWithShare/small.ico new file mode 100644 index 0000000..b3ec03b Binary files /dev/null and b/Samples/SampleChatAppWithShare/SampleChatAppWithShare/small.ico differ diff --git a/Samples/SampleChatAppWithShare/SampleChatAppWithShare/targetver.h b/Samples/SampleChatAppWithShare/SampleChatAppWithShare/targetver.h new file mode 100644 index 0000000..bf75e08 --- /dev/null +++ b/Samples/SampleChatAppWithShare/SampleChatAppWithShare/targetver.h @@ -0,0 +1,6 @@ +#pragma once + +// // Including SDKDDKVer.h defines the highest available Windows platform. +// If you wish to build your application for a previous Windows platform, include WinSDKVer.h and +// set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h. +#include