C ++ 11 + SDL2 + Windows:多线程程序在任何输入事件后挂起(C++11 + SDL2 + Windows: Multithreaded program hangs after any input event)

我正在使用C ++ 11,MinGW和Windows API进行屏幕捕获程序。 我正在尝试使用SDL2来观看我的屏幕捕获程序如何实时工作。

窗口打开很好,程序似乎运行良好,只要我只做移动鼠标光标。 但是如果我点击窗口,窗口外的菜单栏或按任意键,SDL窗口会冻结。

我已经为事件设置了一些日志记录来弄清楚发生了什么。 我从未按顺序接收除SDL_WINDOW_FOCUS_GAINEDSDL_TEXTEDITINGSDL_WINDOWEVENT_SHOWN之外的任何事件。 所有这些都是在一开始就收到的。

我试图找到有关SDL事件处理的教程,因为这是我对问题根源的最佳猜测。 我发现只有基本的事件处理才能观察SDL_QUIT ,基本的鼠标和键盘事件,以及一个似乎没有帮助的SDL_WINDOWEVENT 。 我没有深入了解事件的意义和处理它们的最佳实践。 这可能无关紧要,因为这可能不是问题的根源。 据我所知,SDL正在投入,因为还有其他线程正在运行。

任何人都可以看到我的代码挂起的任何原因,并提供如何解决它的解释?

我的程序结构的快速解释是为了涵盖我省略的代码。 Captor类启动并运行一个线程来获取屏幕截图以传递给EncoderEncoder启动可变数量的线程,从Captor接收屏幕截图,对屏幕截图进行编码,然后将编码传递给Screen 。 传递机制是SynchronousQueue<T>类,它提供配对方法put(const T&)T get()以允许生产者和消费者使用资源进行同步; 这些方法超时以允许系统响应以杀死消息。

现在为源文件(希望没有太多的膨胀)。 虽然我很感激有关如何提高应用程序性能的任何意见,但我的重点是使程序具有响应性。

main.cpp中

#include "RTSC.hpp"

int main(int argc, char** argv) {
    RTSC rtsc {
        (uint32_t) stoi(argv[1]),
        (uint32_t) stoi(argv[2]),
        (uint32_t) stoi(argv[3]),
        (uint32_t) stoi(argv[4]),
        (uint32_t) stoi(argv[5]),
        (uint32_t) stoi(argv[6])
    };

    while (rtsc.isRunning()) {
        SwitchToThread();
    }

    return 0;
}

RTSC.hpp

#ifndef RTSC_HPP
#define RTSC_HPP

#include "Captor.hpp"
#include "Encoder.hpp"
#include "Screen.hpp"

#include <iostream>
using namespace std;


class RTSC {
    private:
        Captor *captor;
        Encoder *encoder;

        SynchronousQueue<uint8_t*> imageQueue {1};
        SynchronousQueue<RegionList> regionQueue {1};

        Screen *screen;

    public:
        RTSC(
            uint32_t width,
            uint32_t height,
            uint32_t maxRegionCount,
            uint32_t threadCount,
            uint32_t divisionsAlongThreadWidth,
            uint32_t divisionsAlongThreadHeight
        ) {
            captor = new Captor(width, height, imageQueue);
            encoder = new Encoder(
                width,
                height,
                maxRegionCount,
                threadCount,
                divisionsAlongThreadWidth,
                divisionsAlongThreadHeight,
                imageQueue,
                regionQueue
            );

            screen = new Screen(
                width,
                height,
                width >> 1,
                height >> 1,
                regionQueue
            );

            captor->start();
        }

        ~RTSC() {
            delete screen;
            delete encoder;
            delete captor;
        }

        bool isRunning() const {
            return screen->isRunning();
        }
};

#endif

Screen.hpp

#ifndef SCREEN_HPP
#define SCREEN_HPP

#include <atomic>
#include <SDL.h>
#include <windows.h>

#include "Region.hpp"
#include "SynchronousQueue.hpp"

using namespace std;

class Screen {
    private:
        atomic_bool running {false};
        HANDLE thread;
        SynchronousQueue<RegionList>* inputQueue;
        uint32_t inputHeight;
        uint32_t inputWidth;
        uint32_t screenHeight;
        uint32_t screenWidth;

        SDL_Renderer* renderer;
        SDL_Surface* surface;
        SDL_Texture* texture;
        SDL_Window* window;

        void run() {
            SDL_Event event;
            while (running) {
                while (SDL_PollEvent(&event)) {
                    switch (event.type) {
                        case SDL_QUIT:
                            running = false;
                            break;

                        case SDL_WINDOWEVENT:
                            switch (event.window.event) {
                                case SDL_WINDOWEVENT_CLOSE:
                                    running = false;
                                    break;

                        default:
                            break;
                    }
                }

                try {
                    RegionList rl = inputQueue->get();

                    SDL_RenderClear(renderer);

                    SDL_LockSurface(surface);
                    SDL_FillRect(surface, nullptr, 0);

                    for (uint32_t i = 0; i < rl.count; ++i) {
                        Region &r = rl.regions[i];

                        SDL_Rect rect {
                            (int) r.getX(),
                            (int) r.getY(),
                            (int) r.getWidth(),
                            (int) r.getHeight()
                        };
                        uint32_t color =
                            (r.getRed() << 16) +
                            (r.getGreen() << 8) +
                            r.getBlue();
                        SDL_FillRect(surface, &rect, color);
                    }

                    SDL_UnlockSurface(surface);
                    SDL_UpdateTexture(
                        texture,
                        nullptr,
                        surface->pixels,
                        surface->pitch
                    );
                    SDL_RenderCopyEx(
                        renderer,
                        texture,
                        nullptr,
                        nullptr,
                        0,
                        nullptr,
                        SDL_FLIP_VERTICAL
                    );
                } catch (exception &e) {}

                SDL_RenderPresent(renderer);
                SwitchToThread();
            }
        }

        static DWORD startThread(LPVOID self) {
            ((Screen*) self)->run();
            return (DWORD) 0;
        }

    public:
        Screen(
            uint32_t inputWidth,
            uint32_t inputHeight,
            uint32_t windowWidth,
            uint32_t windowHeight,
            SynchronousQueue<RegionList> &inputQueue
        ): inputQueue {&inputQueue}, inputHeight {inputHeight} {
            SDL_Init(SDL_INIT_VIDEO);

            window = SDL_CreateWindow(
                "RTSC",
                SDL_WINDOWPOS_CENTERED,
                SDL_WINDOWPOS_CENTERED,
                windowWidth,
                windowHeight,
                SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE |
                    SDL_WINDOW_INPUT_FOCUS | SDL_WINDOW_MOUSE_FOCUS
            );

            renderer = SDL_CreateRenderer(window, -1, 0);

            surface = SDL_CreateRGBSurface(
                0,
                inputWidth,
                inputHeight,
                24,
                0xFF << 16,
                0xFF << 8,
                0xFF,
                0
            );

            texture = SDL_CreateTexture(
                renderer,
                surface->format->format,
                SDL_TEXTUREACCESS_STREAMING,
                inputWidth,
                inputHeight
            );

            running = true;
            thread = CreateThread(nullptr, 0, startThread, this, 0, nullptr);
        }

        ~Screen() {
            running = false;
            WaitForSingleObject(thread, INFINITE);
            CloseHandle(thread);

            SDL_FreeSurface(surface);
            SDL_DestroyRenderer(renderer);
            SDL_DestroyWindow(window);
            SDL_Quit();
        }

        bool isRunning() const {
            return running;
        }
};

#endif

I am working on a screen capture program using C++11, MinGW, and the Windows API. I am trying to use SDL2 to watch how my screen capture program works in real time.

The window opens fine, and the program seems to run well so long as I do nothing more than move the mouse cursor. But iff I click in the window, its menu bar, outside the window, or press any keys, the SDL window freezes.

I have set up some logging for the events to figure out what is happening. I never receive any events other than SDL_WINDOW_FOCUS_GAINED, SDL_TEXTEDITING, and SDL_WINDOWEVENT_SHOWN in that order. All of these are received at the start.

I have tried to find tutorials on SDL event handling since that's my best guess as to the source of the problem. I have found nothing more than basic event handling to watch for SDL_QUIT, basic mouse and keyboard events, and one on SDL_WINDOWEVENTs that does not seem to help. I have found nothing in-depth on what the events mean and best practices for handling them. That may not matter, because that might not be the source of the problem. For all I know, SDL is throwing a fit because there are other threads running.

Can anyone see any cause for this hanging in my code and provide an explanation as to how to fix it?

A quick explanation for the structure of my program is in order to cover the code I have omitted. The Captor class starts and runs a thread to grab a screenshot to pass to the Encoder. The Encoder starts a variable number of threads that receive a screenshot from the Captor, encode the screenshot, then passes the encoding to the Screen. The passing mechanism is the SynchronousQueue<T> class that provides paired methods put(const T&) and T get() to allow a producer and a consumer to synchronize using a resource; these methods time out to allow the the system to be responsive to kill messages.

Now for the source files (hopefully without too much bloat). While I would appreciate any comments on how to improve the performance of the application, my focus is on making the program responsive.

main.cpp

#include "RTSC.hpp"

int main(int argc, char** argv) {
    RTSC rtsc {
        (uint32_t) stoi(argv[1]),
        (uint32_t) stoi(argv[2]),
        (uint32_t) stoi(argv[3]),
        (uint32_t) stoi(argv[4]),
        (uint32_t) stoi(argv[5]),
        (uint32_t) stoi(argv[6])
    };

    while (rtsc.isRunning()) {
        SwitchToThread();
    }

    return 0;
}

RTSC.hpp

#ifndef RTSC_HPP
#define RTSC_HPP

#include "Captor.hpp"
#include "Encoder.hpp"
#include "Screen.hpp"

#include <iostream>
using namespace std;


class RTSC {
    private:
        Captor *captor;
        Encoder *encoder;

        SynchronousQueue<uint8_t*> imageQueue {1};
        SynchronousQueue<RegionList> regionQueue {1};

        Screen *screen;

    public:
        RTSC(
            uint32_t width,
            uint32_t height,
            uint32_t maxRegionCount,
            uint32_t threadCount,
            uint32_t divisionsAlongThreadWidth,
            uint32_t divisionsAlongThreadHeight
        ) {
            captor = new Captor(width, height, imageQueue);
            encoder = new Encoder(
                width,
                height,
                maxRegionCount,
                threadCount,
                divisionsAlongThreadWidth,
                divisionsAlongThreadHeight,
                imageQueue,
                regionQueue
            );

            screen = new Screen(
                width,
                height,
                width >> 1,
                height >> 1,
                regionQueue
            );

            captor->start();
        }

        ~RTSC() {
            delete screen;
            delete encoder;
            delete captor;
        }

        bool isRunning() const {
            return screen->isRunning();
        }
};

#endif

Screen.hpp

#ifndef SCREEN_HPP
#define SCREEN_HPP

#include <atomic>
#include <SDL.h>
#include <windows.h>

#include "Region.hpp"
#include "SynchronousQueue.hpp"

using namespace std;

class Screen {
    private:
        atomic_bool running {false};
        HANDLE thread;
        SynchronousQueue<RegionList>* inputQueue;
        uint32_t inputHeight;
        uint32_t inputWidth;
        uint32_t screenHeight;
        uint32_t screenWidth;

        SDL_Renderer* renderer;
        SDL_Surface* surface;
        SDL_Texture* texture;
        SDL_Window* window;

        void run() {
            SDL_Event event;
            while (running) {
                while (SDL_PollEvent(&event)) {
                    switch (event.type) {
                        case SDL_QUIT:
                            running = false;
                            break;

                        case SDL_WINDOWEVENT:
                            switch (event.window.event) {
                                case SDL_WINDOWEVENT_CLOSE:
                                    running = false;
                                    break;

                        default:
                            break;
                    }
                }

                try {
                    RegionList rl = inputQueue->get();

                    SDL_RenderClear(renderer);

                    SDL_LockSurface(surface);
                    SDL_FillRect(surface, nullptr, 0);

                    for (uint32_t i = 0; i < rl.count; ++i) {
                        Region &r = rl.regions[i];

                        SDL_Rect rect {
                            (int) r.getX(),
                            (int) r.getY(),
                            (int) r.getWidth(),
                            (int) r.getHeight()
                        };
                        uint32_t color =
                            (r.getRed() << 16) +
                            (r.getGreen() << 8) +
                            r.getBlue();
                        SDL_FillRect(surface, &rect, color);
                    }

                    SDL_UnlockSurface(surface);
                    SDL_UpdateTexture(
                        texture,
                        nullptr,
                        surface->pixels,
                        surface->pitch
                    );
                    SDL_RenderCopyEx(
                        renderer,
                        texture,
                        nullptr,
                        nullptr,
                        0,
                        nullptr,
                        SDL_FLIP_VERTICAL
                    );
                } catch (exception &e) {}

                SDL_RenderPresent(renderer);
                SwitchToThread();
            }
        }

        static DWORD startThread(LPVOID self) {
            ((Screen*) self)->run();
            return (DWORD) 0;
        }

    public:
        Screen(
            uint32_t inputWidth,
            uint32_t inputHeight,
            uint32_t windowWidth,
            uint32_t windowHeight,
            SynchronousQueue<RegionList> &inputQueue
        ): inputQueue {&inputQueue}, inputHeight {inputHeight} {
            SDL_Init(SDL_INIT_VIDEO);

            window = SDL_CreateWindow(
                "RTSC",
                SDL_WINDOWPOS_CENTERED,
                SDL_WINDOWPOS_CENTERED,
                windowWidth,
                windowHeight,
                SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE |
                    SDL_WINDOW_INPUT_FOCUS | SDL_WINDOW_MOUSE_FOCUS
            );

            renderer = SDL_CreateRenderer(window, -1, 0);

            surface = SDL_CreateRGBSurface(
                0,
                inputWidth,
                inputHeight,
                24,
                0xFF << 16,
                0xFF << 8,
                0xFF,
                0
            );

            texture = SDL_CreateTexture(
                renderer,
                surface->format->format,
                SDL_TEXTUREACCESS_STREAMING,
                inputWidth,
                inputHeight
            );

            running = true;
            thread = CreateThread(nullptr, 0, startThread, this, 0, nullptr);
        }

        ~Screen() {
            running = false;
            WaitForSingleObject(thread, INFINITE);
            CloseHandle(thread);

            SDL_FreeSurface(surface);
            SDL_DestroyRenderer(renderer);
            SDL_DestroyWindow(window);
            SDL_Quit();
        }

        bool isRunning() const {
            return running;
        }
};

#endif

原文:https://stackoverflow.com/questions/22058447
2024-04-25 21:04

满意答案

我想你可以使用它从你的相对路径获得一个绝对路径:

ServletContext ctx = ....;
String absolutePath = ctx.getRealPath("/WEB-INF/" + relativePath);

Figured it out...

WTP uses the variable "${catalina.home}", not "${CATALINA_HOME}"...

相关问答

更多

Eclipse WTP与Tomcat的干净和干净的工作目录之间的区别(difference between clean and clean working directory in Eclipse WTP with Tomcat)

清理:清除所有webapps并重新部署它们(在Tomcat实例运行时不要这样做) 清理工作目录:清除Tomcat的工作目录,例如编译的JSP所在的位置。 看到这里 。 Clean: Purges all webapps and redeploys them (don't do this while the Tomcat instance is running) Clean working dir: Cleans out Tomcat's work dir, e.g. where compiled ...

使用eclipse wtp将web项目发布到tomcat之后缺少类(missing classes after publish web project into tomcat using eclipse wtp)

这发生在我身上很多。 我不会称之为巫毒。 我认为当你在后台更改东西时Eclipse WTP不能很好地工作(例如Maven构建)。 我做什么来解决这个问题是为了避免完全使用它。 相反,我使用Maven WAR插件来部署应用程序: mvn war:inplace tomcat:inplace -DskipTests=true 这个工作非常快,因为它不需要组装和打包战争。 然后取消部署应用程序: mvn tomcat:undeploy 我有脚本 部署并启动托马斯 取消部署并停止tomcat 它看起来...

Eclipse WTP:如何在Tomcat上启用SSL?(Eclipse WTP: How do I enable SSL on Tomcat?)

如果您已经创建了服务器,可以编辑它复制的server.xml模板。 如果您使用项目资源管理器,它位于其他项目 - >服务器 - > Tomcat服务器名称 - > server.xml下 If you've already created the server, you can edit the server.xml template it copies. If you use the project explorer, It is under Other Projects->Servers->T...

从Eclipse WTP发布到服务器时如何排除Maven资源?(How do I exclude Maven resources when publishing from Eclipse WTP to a server?)

@格雷格, 要考虑的一个选择是让Maven而不是Eclipse WTP将WAR推送到服务器。 这可能需要权衡取决于Eclipse在幕后为您做什么。 然后,您的桌面将更准确地反映CI构建中发生的情况。 无论如何, 本指南深入解释了如何配置tomcat7:deploy目标。 这是一个建议的pom.xml片段,可以帮助您入门: <plugins> <plugin> <groupId>org.apache.tomcat.maven</groupId> <artifa...

Eclipse的WTP转换输出(Eclipse's WTP translation output)

现在你必须添加单独的自动测试下载来执行此操作,并且仅在3.1分支中,但它通过Ctrl + Shift + 9启用“显示翻译”命令。 请注意,生成的转换不是服务器在运行时创建的100%相同 - 它不打算执行。 此外,最新的3.0.3版本包含对翻译器的修复,应该清除这些类型的问题(NESTED变量+自动关闭标记)。 3.0.3将于11月到期,并应该干净地更新到Ganymede SR1。 Right now you'd have to add the separate automated tests d...

使用Eclipse的WTP Tomcat服务器调试servlet时,在web.xml中指定相对路径?(Specifying a relative path in web.xml when debugging a servlet with Eclipse's WTP Tomcat Server?)

我想你可以使用它从你的相对路径获得一个绝对路径: ServletContext ctx = ....; String absolutePath = ctx.getRealPath("/WEB-INF/" + relativePath); Figured it out... WTP uses the variable "${catalina.home}", not "${CATALINA_HOME}"...

如何在Eclipse中检查WTP的版本?(How to check version of WTP in Eclipse?)

据我所知,版本可以位于“功能名称”是“Eclipse Web开发人员工具”的行中 看屏幕截图: As far as I know the version can be located in the row where the "Feature Name" is "Eclipse Web Developer Tools" See screen capture:

WTP如何确定Tomcat版本(How does WTP determine the Tomcat version)

看起来像anwser在这里: https : //github.com/eclipse/webtools.servertools/blob/master/plugins/org.eclipse.jst.server.tomcat.core/tomcatcore/org/eclipse/jst/server/tomcat /core/internal/TomcatVersionHelper.java 有趣的是,他们不检查外部文件CATALINA_BASE/lib/org/apache/catalin...

无法将servlet添加到Tomcat web.xml [关闭](Cannot add servlet to Tomcat web.xml [closed])

您不会将web.xml添加到Tomcat。 你可以在你的jar中部署(放置)包含web-INF下的web.xml的整个战争。 您可以在Tomcat webapps文件夹中放置war文件(通常),也可以在your.tomcat.address / manager下使用其管理器 You don't add web.xml to your Tomcat. You deploy (place) the whole war which includes web.xml under WEB-INF insid...

在哪里下载Eclipse WTP(Where to download Eclipse WTP)

转到帮助 - > Eclipse Market Place 并搜索WTP并安装它。 希望这可以帮助:) Go to Help->Eclipse Market Place and search for WTP and install it. Hope this helps:)

相关文章

更多

《编程之道 Java》(Program JAVA)[ISO]

本教材的第1章到第7章讲述了怎样学习Java和Java的基础知识,以及面向对象的编程思想;第8章到第1 ...

A Great List of Windows Tools

Windowsis an extremely effective and a an efficient ...

摘抄---Multimedia Streaming on Microsoft Windows CE 3.0

Multimedia Streaming on Microsoft Windows CE 3.0 ...

WINDOWS渗透与提权总结(2)

vbs下载者: ...

Solr参数(DisMax Event Facet)

一.DisMax 1.tie:query and init param for tiebreaker ...

windows系统安装MongoDB

安装 下载MongoDB的安装包:mongodb-win32-x86_64-2008plus-ssl- ...

solr部署(windows)

前提: 1.装好JRE和JDK,设置环境变量 JAVA_HOME C:\Program File ...

Windows下 建立solr时 出现 问题解答(转 )

使用solr搭建你的全文检索 Solr 是一个可供企业使用的、基于 Lucene 的开箱即用的搜索 ...

Windows Phone Emoji

今天基于项目的需要,研究了一下windows phone 8里面的Emoji实现。如果大家用过wp版本 ...

Jordan 11 magic reversal

Jordan 11 Columbia Opening, the Magic give the Lake ...

最新问答

更多

获取MVC 4使用的DisplayMode后缀(Get the DisplayMode Suffix being used by MVC 4)

我用Google搜索了一个解决方案。 “EnumDisplayModeProvider”是我自己设置网站的各种模式的枚举。 public EnumDisplayModeProvider GetDisplayModeId() { foreach (var mode in DisplayModeProvider.Instance.Modes) if (mode.CanHandleContext(HttpContext)) {

如何通过引用返回对象?(How is returning an object by reference possible?)

这相对简单:在类的构造函数中,您可以分配内存,例如使用new 。 如果你制作一个对象的副本,你不是每次都分配新的内存,而是只复制指向原始内存块的指针,同时递增一个也存储在内存中的引用计数器,使得每个副本都是对象可以访问它。 如果引用计数降至零,则销毁对象将减少引用计数并仅释放分配的内存。 您只需要一个自定义复制构造函数和赋值运算符。 这基本上是共享指针的工作方式。 This is relatively easy: In the class' constructor, you allocate m

矩阵如何存储在内存中?(How are matrices stored in memory?)

正如它在“熵编码”中所说的那样,使用Z字形图案,与RLE一起使用,在许多情况下,RLE已经减小了尺寸。 但是,据我所知,DCT本身并没有给出稀疏矩阵。 但它通常会增强矩阵的熵。 这是compressen变得有损的点:输入矩阵用DCT传输,然后量化量化然后使用霍夫曼编码。 As it says in "Entropy coding" a zig-zag pattern is used, together with RLE which will already reduce size for man

每个请求的Java新会话?(Java New Session For Each Request?)

你是如何进行重定向的? 您是否事先调用了HttpServletResponse.encodeRedirectURL()? 在这里阅读javadoc 您可以使用它像response.sendRedirect(response.encodeRedirectURL(path)); The issue was with the path in the JSESSIONID cookie. I still can't figure out why it was being set to the tomca

css:浮动div中重叠的标题h1(css: overlapping headlines h1 in floated divs)

我认为word-break ,如果你想在一个单词中打破行,你可以指定它,这样做可以解决问题: .column { word-break:break-all; } jsFiddle演示。 您可以在此处阅读有关word-break属性的更多信息。 I think word-break, with which you can specify if you want to break line within a word, will do the trick: .column { word-break

无论图像如何,Caffe预测同一类(Caffe predicts same class regardless of image)

我认为您忘记在分类时间内缩放输入图像,如train_test.prototxt文件的第11行所示。 您可能应该在C ++代码中的某个位置乘以该因子,或者使用Caffe图层来缩放输入(请查看ELTWISE或POWER图层)。 编辑: 在评论中进行了一次对话之后,结果发现在classification.cpp文件中错误地删除了图像均值,而在原始训练/测试管道中没有减去图像均值。 I think you have forgotten to scale the input image during cl

xcode语法颜色编码解释?(xcode syntax color coding explained?)

转到: Xcode => Preferences => Fonts & Colors 您将看到每个语法高亮颜色旁边都有一个简短的解释。 Go to: Xcode => Preferences => Fonts & Colors You'll see that each syntax highlighting colour has a brief explanation next to it.

在Access 2010 Runtime中使用Office 2000校对工具(Use Office 2000 proofing tools in Access 2010 Runtime)

你考虑过第三方拼写检查吗? 您可以将在C#中开发的自定义WinForms控件插入访问数据库吗? VB6控件怎么样? 如果你能找到一个使用第三方库进行拼写检查的控件,那可能会有效。 Have you considered a third party spell checker? Can you insert a custom WinForms controls developed in C# into an access database? What about a VB6 control? If

从单独的Web主机将图像传输到服务器上(Getting images onto server from separate web host)

我有同样的问题,因为我在远程服务器上有两个图像,我需要在每天的预定义时间复制到我的本地服务器,这是我能够提出的代码... try { if(@copy('url/to/source/image.ext', 'local/absolute/path/on/server/' . date("d-m-Y") . ".gif")) { } else { $errors = error_get_last(); throw new Exception($err

从旧版本复制文件并保留它们(旧/新版本)(Copy a file from old revision and keep both of them (old / new revision))

我不确定我完全明白你在说什么。 你能编辑你的帖子并包含你正在做的Subversion命令/操作的特定顺序吗? 最好使用命令行svn客户端,以便容易为其他人重现问题。 如果您只是想获取文件的旧副本(即使该文件不再存在),您可以使用如下命令: svn copy ${repo}/trunk/moduleA/file1@${rev} ${repo}/trunk/moduleB/file1 其中${repo}是您的存储库的URL, ${rev}是您想要的文件的版本。 这将恢复该文件的旧版本,包括最高版本